<template>
  <div class="app-container">
    <!-- 内容区 -->
    <div class="content-area">
      <t-list
        ref="contentListRef"
        :scroll="{ rowHeight: 200, bufferSize: 5, threshold: 5 }"
        class="content-list-container"
        split
        @scroll="handleListScroll"
      >
        <t-list-item 
          v-for="(question, index) in currentQuestions" 
          :key="question.id"
          @click="handleQuestionClick(question)"
        >
          <div class="question-content">
            <!-- 按原始顺序显示题干、表格和选项 -->
            <template v-for="(item, itemIndex) in extractQuestionInfo(question).orderedItems" :key="itemIndex">
              <!-- 题干显示为h3标签 -->
              <h3 v-if="item.type === 'title'">{{ item.content }}</h3>
              
              <!-- 表格显示为TDesign表格，默认小尺寸、不显示斑马纹和边框 -->
              <div v-else-if="item.type === 'table'" class="markdown-table-wrapper">
                <t-table 
                  :data="item.tableData.data" 
                  :columns="item.tableData.columns"
                  size="small"
                  :stripe="false"
                  :bordered="false"
                  row-key="id"
                ></t-table>
              </div>
              
              <!-- 选项显示为带字母前缀的div -->
              <div v-else-if="item.type === 'option'" class="option-item">
                {{ getOptionLabel(item, extractQuestionInfo(question).orderedItems) }}. {{ item.content }}
              </div>
            </template>
            
            <div class="analysis" v-if="question.showAnalysis">
              <div class="analysis-container">
                <div class="analysis-content">
                  <p class="simple-analysis">{{ formatText(question.simpleAnswer) || '正在生成解析...' }}</p>
                  <p class="detailed-analysis">{{ formatText(question.answer) || '' }}</p>
                </div>
              </div>
            </div>
            <div class="question-stars">
              <t-rate 
                v-model="question.stars" 
                :count="2" 
                :color="question.stars === 1 ? 'grey' : undefined"
                @change="(value) => handleStarsChange(question, value)"
              />
              <div class="question-buttons">
                <t-button 
                  v-if="question.showAnalysis"
                  variant="text"
                  @click.stop="openEditDialog(question)"
                >
                  <template #icon><pencil fill="gray"/></template>
                </t-button>
                <t-button 
                  v-if="question.showAnalysis"
                  variant="text"
                  @click.stop="retryGenerateAnalysis(question)"
                >
                  <template #icon><refresh fill="gray"/></template>
                </t-button>
              </div>
            </div>
          </div>
        </t-list-item>
      </t-list>
    </div>

    <!-- 配置抽屉 -->
    <t-drawer
      v-model:visible="drawerVisible"
      :footer="false"
      :size="drawerSize"
      class="custom-drawer"
    >
      <t-tabs v-model="activeTab" class="custom-tabs">
        <t-tab-panel value="add" label="添加试题">
          <div class="tab-content">
            <div class="form-list">
              <div class="form-item">
                <div class="question-input-header">
                  <span class="clear-text" @click="newQuestion = ''" style="font-size: 15px;">清空</span>
                </div>
                <t-textarea
                  :autofocus="true"
                  v-model="newQuestion"
                  placeholder="请输入试题内容"
                  class="question-textarea"
                  :autosize="true"
                />
                <div class="parse-options" style="margin-top: 10px;">
                  <t-checkbox v-model="parseLastLineAsAnswer">将每道题的最后一行作为答案</t-checkbox>
                </div>
              </div>
            </div>
          </div>
        </t-tab-panel>
        <t-tab-panel value="paper" label="试卷管理">
          <div class="tab-content">
            <div class="paper-list-header" style="padding-top: 0px;">
              <span class="add-paper-text" @click="handleGetAIAnalysis" style="font-size: 15px; margin-right: 20px;">
                获取AI解析{{ aiAnalysisProgress.total > 0 ? `（${aiAnalysisProgress.processed}/${aiAnalysisProgress.total}）` : '' }}
              </span>
              <span class="add-paper-text" @click="handleAddPaper" style="font-size: 15px;">+ 新增试卷</span>
            </div>
            <t-list split>
              <t-list-item v-if="isAddingPaper">
                <div class="add-paper-row">
                  <t-input :autofocus="true" v-model="newPaperName" placeholder="请输入试卷名称" />
                  <div class="add-paper-actions">
                    <t-button theme="primary" variant="text" @click="confirmAddPaper">确定</t-button>
                    <t-button theme="default" variant="text" @click="cancelAddPaper">取消</t-button>
                  </div>
                </div>
              </t-list-item>
              <t-list-item v-for="paper in papers" :key="paper.id">
                {{ paper.name }}
                <template #action>
                  <t-link theme="primary" hover="color" style="margin-left: 16px" @click="switchPaper(paper)">切换</t-link>
                  <t-link theme="primary" hover="color" style="margin-left: 16px" @click="renamePaper(paper)">重命名</t-link>
                  <t-link theme="danger" hover="color" style="margin-left: 16px" @click="showDeleteDialog(paper)">删除</t-link>
                </template>
              </t-list-item>
            </t-list>
          </div>
        </t-tab-panel>
        <t-tab-panel value="ui" label="界面设置">
          <div class="tab-content">
            <t-space direction="vertical" size="large">
              <t-typography-title level="h4">界面主题</t-typography-title>
              <div style="display: flex; justify-content: space-between; align-items: center;">
                <span style="font-size: 16px;">{{ uiConfig.darkMode ? '暗黑模式' : '明亮模式' }}</span>
                <t-switch
                  v-model="lightMode"
                  size="large"
                >
                  <template #label="{ value }">
                    <template v-if="!value">
                      <moon theme="outline" size="14" fill="#e7e7e7"/>
                    </template>
                    <template v-else>
                      <sun-one theme="outline" size="14" fill="#e7e7e7"/>
                    </template>
                  </template>
                </t-switch>
              </div>
              
              <!-- 暗黑模式下的文字透明度设置 -->
              <div v-if="uiConfig.darkMode" style="margin-top: 16px;">
                <span style="font-size: 16px;">暗黑模式时文字的透明度</span>
                <div style="display: flex; justify-content: flex-end; align-items: center;">
                  <span style="font-size: 14px; color: #999; text-align: right;">{{ uiConfig.textOpacity }}%</span>
                </div>
                <t-slider 
                  v-model="uiConfig.textOpacity" 
                  :min="0" 
                  :max="70" 
                  :step="1"
                  :marks="textOpacityMarks"
                  :show-tooltip="true"
                  @change="handleTextOpacityChange"
                />
              </div>
              
              <t-typography-title level="h4">文字大小</t-typography-title>
              <div class="font-size-slider">
                <t-slider 
                  v-model="uiConfig.fontSize" 
                  :min="-4" 
                  :max="6" 
                  :step="1" 
                  :marks="fontSizeMarks" 
                  :show-tooltip="true"
                  @change="handleFontSizeChange"
                />
                <div class="font-size-demo">
                  <span :style="{fontSize: `${18 + uiConfig.fontSize}px`}">示例文字</span>
                </div>
              </div>
            </t-space>
          </div>
        </t-tab-panel>
        <t-tab-panel value="service" label="服务配置">
          <div class="tab-content">
            <t-space direction="vertical" size="large">
                <t-typography-title level="h4">Siliconflow 服务
                </t-typography-title>
              <!-- API Token 输入框 -->
              <t-input-adornment prepend="Token">

              <t-input
                v-model="serviceConfig.token"
                placeholder="请输入 API Token"
                @change="handleServiceConfigChange"
              >
              </t-input>
              </t-input-adornment>
              <!-- 模型名称输入框 -->
              <t-input-adornment prepend="Model">
                <t-input
                  v-model="serviceConfig.model"
                  placeholder="模型名称"
                  @change="handleServiceConfigChange"
                />
              </t-input-adornment>

              <!-- 引导语句文本框 -->
              <t-textarea
                v-model="serviceConfig.prompt"
                placeholder="请输入引导语句"
                :autosize="{ minRows: 3, maxRows: 12 }"
                @change="handleServiceConfigChange"
              />
            <t-typography-title level="h4">S3存储同步服务
                <t-button 
                  :variant="syncConfig.syncEnabled ? 'outline' : 'dashed'" 
                  theme="default"
                  style="float: right; margin-left: 8px; font: var(--td-font-body-medium);"
                  @click="handleSyncToggle"
                >
                  {{ syncConfig.syncEnabled ? '已启用' : '未启用' }}
                </t-button>
            </t-typography-title>
            


                <t-input-adornment prepend="Endpoint">
                  <t-input
                    v-model="syncConfig.s3Endpoint"
                    placeholder="例如: https://xxxx.r2.cloudflarestorage.com"
                    @change="handleSyncConfigChange"
                  />
                </t-input-adornment>
                <t-input-adornment prepend="Region">
                  <t-input
                    v-model="syncConfig.s3Region"
                    placeholder="区域，例如'ap-beijing'，Cloudflare R2使用'auto'"
                    @change="handleSyncConfigChange"
                  />
                </t-input-adornment>
                <t-input-adornment prepend="Access Key ID">
                  <t-input
                    v-model="syncConfig.s3AccessKeyId"
                    placeholder="Access Key ID"
                    @change="handleSyncConfigChange"
                  />
                </t-input-adornment>
                <t-input-adornment prepend="Secret Access Key">
                  <t-input
                    v-model="syncConfig.s3SecretAccessKey"
                    type="password"
                    placeholder="Secret Access Key"
                    @change="handleSyncConfigChange"
                  />
                </t-input-adornment>
                <t-input-adornment prepend="Bucket">
                  <t-input
                    v-model="syncConfig.s3Bucket"
                    placeholder="存储桶名称"
                    @change="handleSyncConfigChange"
                  />
                </t-input-adornment>
                
                <!-- 添加手动同步按钮 -->
                <div style="margin-top: 16px; display: flex; gap: 12px;">
                  <t-button theme="primary" @click="manualSync" :loading="syncLoading" style="flex: 1;">
                    同步
                  </t-button>
                </div>

            </t-space>
          </div>
        </t-tab-panel>
      </t-tabs>
      <div class="drawer-footer">
        <template v-if="activeTab === 'add'">
          <t-button theme="primary" @click="saveConfig" style="font: var(--td-font-body-medium);">添加</t-button>
          <t-button theme="default" variant="outline" @click="drawerVisible = false" style="font: var(--td-font-body-medium);">返回</t-button>
        </template>
        <template v-else>
          <div style="flex: 1"></div>
          <t-button theme="default" variant="outline" @click="drawerVisible = false" style="font: var(--td-font-body-medium);">返回</t-button>
        </template>
      </div>
    </t-drawer>

    <!-- 删除确认对话框 -->
    <t-dialog
      v-model:visible="deleteDialogVisible"
      header="确认删除"
      width="40%"
      theme="warning"
      :on-confirm="handleDeletePaper"
      :on-cancel="() => { deleteDialogVisible = false; paperToDelete = null }"
    >
      <p>确认删除试卷"{{ paperToDelete?.name }}"？</p>
    </t-dialog>

    <!-- 重命名对话框 -->
    <t-dialog
      v-model:visible="renameDialogVisible"
      header="重命名试卷"
      width="40%"
      :on-confirm="confirmRenamePaper"
      :on-cancel="() => { renameDialogVisible = false; paperToRename = null; newPaperName = '' }"
    >
      <t-input v-model="newPaperName" placeholder="请输入新的试卷名称" />
    </t-dialog>

    <!-- 删除题目编辑对话框，改用抽屉组件 -->
    <t-drawer
      v-model:visible="editQuestionDialogVisible"
      header="编辑题目"
      :size="drawerSize"
      :footer="false"
      class="custom-drawer"
    >
      <div class="tab-content" style="height: calc(100vh - 160px); padding-top: 0;">
        <div class="form-list">
          <div class="form-item">
            <div class="question-input-header">
              <span class="clear-text no-underline" @click="editingQuestionContent = ''" style="font-size: 15px;">清空</span>
            </div>
            <t-textarea
              :autofocus="true"
              v-model="editingQuestionContent"
              placeholder="请编辑题目内容，清空后点击确认可删除题目"
              class="question-textarea"
              :autosize="true"
            />
          </div>
        </div>
      </div>
      <div class="drawer-footer">
        <t-button theme="primary" @click="confirmEditQuestion" style="font: var(--td-font-body-medium);">确认</t-button>
        <t-button theme="default" variant="outline" @click="cancelEditQuestion" style="font: var(--td-font-body-medium);">取消</t-button>
      </div>
    </t-drawer>

    <!-- 删除题目确认对话框 -->
    <t-dialog
      v-model:visible="deleteQuestionDialogVisible"
      header="确认删除"
      width="40%"
      theme="warning"
      :on-confirm="confirmDeleteQuestion"
      :on-cancel="cancelDeleteQuestion"
    >
      <p>确认要删除此题目吗？此操作不可恢复。</p>
    </t-dialog>

    <!-- 备份恢复对话框 - 保留但隐藏 -->
    <t-dialog
      v-model:visible="restoreDialogVisible"
      header="恢复备份"
      width="40%"
      :on-confirm="handleRestoreBackup"
      :on-cancel="cancelRestore"
      v-if="false"
    >
      <div class="backup-list">
        <t-list split>
          <t-list-item v-for="backup in backupsList" :key="backup.key" @click="selectBackup(backup)">
            <div class="backup-item">
              <div class="backup-info">
                <div class="backup-name">{{ backup.key }}</div>
                <div class="backup-details">
                  <div class="backup-size">{{ backup.size }}</div>
                  <div class="backup-date">{{ backup.date }}</div>
                </div>
              </div>
              <div class="backup-actions">
                <t-checkbox v-model="backup.selected"></t-checkbox>
              </div>
            </div>
          </t-list-item>
        </t-list>
      </div>
      <div class="dialog-footer">
        <t-button theme="primary" @click="handleRestoreBackup" :loading="restoreLoading">恢复</t-button>
        <t-button theme="default" @click="cancelRestore">取消</t-button>
      </div>
    </t-dialog>

    <!-- 悬浮工具栏 -->
    <t-sticky-tool
      ref="stickyToolRef"
      class="sticky-tool"
      shape="round"
      type="compact"
      @click="handleStickyClick"
    >
      <t-sticky-item label="拖动" class="drag-handle">
        <template #icon><drag style="font-size: 24px;"/></template>
      </t-sticky-item>
      <t-sticky-item v-if="hasPendingSync || syncError" label="同步" class="sticky-item sync-button" @click.stop="handleSyncClick">
        <template #icon><upload-three :theme="syncError ? 'outline' : (syncLoading ? 'filled' : 'outline')" :fill="syncError ? '#e34d59' : undefined" style="font-size: 24px;"/></template>
      </t-sticky-item>
      <t-sticky-item label="设置" class="sticky-item" @click.stop="handleSettingsClick" @touchend.stop.prevent>
        <template #icon><config style="font-size: 24px;"/></template>
      </t-sticky-item>
    </t-sticky-tool>
  </div>
</template>

<script setup lang="jsx">
import { ref, onMounted, computed, onUnmounted, nextTick, watch } from 'vue'
import { openDB } from 'idb'
import { Config, Drag, LockOne, Pencil, Refresh, DarkMode, SunOne, Moon, UploadThree } from '@icon-park/vue-next'
import { MessagePlugin, NotifyPlugin } from 'tdesign-vue-next'
import { v4 as uuidv4 } from 'uuid'
import interactjs from 'interactjs'
import { S3Client, PutObjectCommand, ListObjectsV2Command, GetObjectCommand, DeleteObjectCommand } from '@aws-sdk/client-s3'

// 合并TextConverterService代码
/**
 * 将文本格式的题目内容转换为CSV格式
 * @param {string} text - 要转换的文本内容
 * @returns {string} - CSV格式的文本内容
 */
function convertTextToCSV(text) {
  if (!text) return '';

  // 分割文本为行
  const lines = text.split(/\r?\n/);
  const csvFields = [];  // 存储CSV字段
  let currentIndex = 0;
  
  /**
   * 转义CSV字段中的特殊字符
   * @param {string} field - 需要转义的字段内容
   * @returns {string} - 转义后的内容
   */
  function escapeCSVField(field) {
    if (!field) return '';
    
    // 如果字段包含逗号、双引号或换行符，需要进行转义
    if (field.includes(',') || field.includes('"') || field.includes('\n') || field.includes('\r')) {
      // 防止字段内容中可能包含下一个字段的标识符
      let escapedField = field.replace(/题干$/, ' 题干').replace(/选项$/, ' 选项').replace(/表格$/, ' 表格');
      // 将字段中的双引号替换为两个双引号（CSV规范的转义方式）
      escapedField = escapedField.replace(/"/g, '""');
      // 用双引号包裹整个字段
      return `"${escapedField}"`;
    }
    
    return field;
  }
  
  /**
   * 添加CSV字段
   * @param {string} type - 字段类型（题干或选项）
   * @param {string} content - 字段内容
   */
  function addCSVField(type, content) {
    if (content && content.trim()) {
      csvFields.push(type);
      csvFields.push(escapeCSVField(content.trim()));
    }
  }
  
  /**
   * 判断一行是否是选项格式
   * @param {string} line - 需要检查的行文本
   * @returns {boolean} - 是否为选项
   */
  function isOption(line) {
    // 识别 "A. 内容" 格式
    if (line.match(/^[A-Z]\.\s*/) !== null) {
      return true;
    }
    // 识别 "- A. 内容" 或 "   - A. 内容" 格式（带有前导空格和破折号的Markdown风格选项）
    if (line.match(/^\s*-\s*[A-Z]\.\s*/) !== null) {
      return true;
    }
    return false;
  }

  /**
   * 移除题号
   * @param {string} text - 包含题号的文本
   * @returns {string} - 移除题号后的文本
   */
  function removeQuestionNumber(text) {
    // 移除形如"123."的题号
    const numberMatch = text.match(/^\d+\.\s*(.*)/);
    if (numberMatch) {
      return numberMatch[1].trim();
    }
    return text.trim();
  }
  
  /**
   * 检查是否是中文括号包围的行
   * @param {string} line - 需要检查的行文本
   * @returns {boolean} - 是否被中文括号包围
   */
  function isChineseBracketWrapped(line) {
    // 检查是否以中文括号开始和结束，如（XXX）
    return line.startsWith('（') && line.endsWith('）');
  }
  
  /**
   * 判断一行是否是Markdown表格行
   * @param {string} line - 需要检查的行文本
   * @returns {boolean} - 是否为Markdown表格行
   */
  function isTableRow(line) {
    // Markdown表格行通常以 | 开始并以 | 结束
    return line.trim().startsWith('|') && line.trim().endsWith('|');
  }
  
  /**
   * 判断一行是否是Markdown表格分隔行
   * @param {string} line - 需要检查的行文本
   * @returns {boolean} - 是否为Markdown表格分隔行
   */
  function isTableSeparator(line) {
    // 表格分隔行通常包含 ----- 和 |
    return isTableRow(line) && line.includes('---');
  }
  
  /**
   * 检测并提取Markdown表格
   * @param {number} startIndex - 开始检查的行索引
   * @returns {Object} - 包含表格内容和新的当前索引
   */
  function extractTable(startIndex) {
    let tableContent = [];
    let currentLineIndex = startIndex;
    let tableFound = false;
    
    // 检查是否是表格开始行
    if (!isTableRow(lines[currentLineIndex])) {
      return { tableContent: null, newIndex: startIndex };
    }
    
    // 收集表格行
    while (currentLineIndex < lines.length) {
      const currentLine = lines[currentLineIndex].trim();
      
      // 如果是空行或不是表格行，则表格结束
      if (currentLine === '' || !isTableRow(currentLine)) {
        break;
      }
      
      // 如果是表格分隔行（---|---），确认这是一个Markdown表格
      if (isTableSeparator(currentLine)) {
        tableFound = true;
      }
      
      tableContent.push(currentLine);
      currentLineIndex++;
    }
    
    // 如果没有找到分隔行，则不是规范的Markdown表格
    if (!tableFound || tableContent.length < 3) {
      return { tableContent: null, newIndex: startIndex };
    }
    
    return { 
      tableContent: tableContent.join('\n'), 
      newIndex: currentLineIndex 
    };
  }
  
  /**
   * 处理表格并添加字段
   * @param {string} tableContent - 表格内容
   * @returns {boolean} - 是否成功处理了表格
   */
  function handleTable(tableContent) {
    if (tableContent) {
      // 特殊处理表格内容 - 转义双引号并用引号包裹
      let escapedTable = tableContent.replace(/"/g, '""');
      csvFields.push('表格');
      csvFields.push(`"${escapedTable}"`);
      return true;
    }
    return false;
  }
  
  while (currentIndex < lines.length) {
    let line = lines[currentIndex].trim();
    
    // 跳过空行
    if (line === '') {
      currentIndex++;
      continue;
    }
    
    // 跳过以中文括号开始和结尾的行
    if (isChineseBracketWrapped(line)) {
      currentIndex++;
      continue;
    }
    
    // 处理分隔符 - 用于识别题目边界
    if (line === '---') {
      // 题目分隔符，不添加到CSV中
      currentIndex++;
      continue;
    }
    
    // 先检查是否是表格开始行
    if (isTableRow(line)) {
      const { tableContent, newIndex } = extractTable(currentIndex);
      
      // 如果找到了有效的表格
      if (tableContent) {
        // 处理表格内容
        handleTable(tableContent);
        
        // 更新索引到表格后的位置
        currentIndex = newIndex;
        continue;
      }
    }
    
    // 检查是否是共用备选答案的题目组
    if (line.match(/[（(]\d+\s*-\s*\d+\s*题共用备选答案[）)]/)) {
      // 添加标题
      addCSVField('题干', line);
      
      // 寻找选项
      currentIndex++;
      
      // 跳过可能的空行
      while (currentIndex < lines.length && lines[currentIndex].trim() === '') {
        currentIndex++;
      }
      
      // 收集选项和题干
      while (currentIndex < lines.length) {
        let nextLine = lines[currentIndex].trim();
        
        // 如果是空行，跳过
        if (nextLine === '') {
          currentIndex++;
          continue;
        }
        
        // 跳过以中文括号开始和结尾的行
        if (isChineseBracketWrapped(nextLine)) {
          currentIndex++;
          continue;
        }
        
        // 如果是分隔符，退出选项收集
        if (nextLine === '---') {
          break;
        }
        
        // 检查是否是表格开始行
        if (isTableRow(nextLine)) {
          const { tableContent, newIndex } = extractTable(currentIndex);
          
          // 如果找到了有效的表格
          if (tableContent && handleTable(tableContent)) {
            // 更新索引到表格后的位置
            currentIndex = newIndex;
            continue;
          }
        }
        
        // 检查是否是选项格式
        if (isOption(nextLine)) {
          // 处理标准格式 "A. 内容"
          let optionMatch = nextLine.match(/^[A-Z]\.\s*(.*)/);
          if (optionMatch) {
            // 只存储选项内容，忽略字母前缀
            addCSVField('选项', optionMatch[1]);
          } else {
            // 处理Markdown格式 "- A. 内容" 或 "   - A. 内容"
            optionMatch = nextLine.match(/^\s*-\s*[A-Z]\.\s*(.*)/);
            if (optionMatch) {
              addCSVField('选项', optionMatch[1]);
            }
          }
          currentIndex++;
        } else {
          // 如果不是选项格式，作为题干处理
          // 移除可能存在的题号
          addCSVField('题干', removeQuestionNumber(nextLine));
          currentIndex++;
        }
      }
    }
    // 跳过题目共用标记行（包含括号和题号范围的行）
    else if (line.match(/（\s*\d+\s*-\s*\d+\s*题共用题干\s*）/)) {
      // 跳过这一行，不添加到CSV中
      currentIndex++;
      continue;
    }
    // 处理单个题目
    else if (line.match(/^\d+\.\s*/)) {
      // 添加题目内容，移除题号
      addCSVField('题干', removeQuestionNumber(line));
      
      // 寻找选项
      currentIndex++;
      
      // 跳过可能的空行
      while (currentIndex < lines.length && lines[currentIndex].trim() === '') {
        currentIndex++;
      }
      
      // 收集选项
      while (currentIndex < lines.length) {
        let optionLine = lines[currentIndex].trim();
        
        // 如果是空行，跳过
        if (optionLine === '') {
          currentIndex++;
          continue;
        }
        
        // 跳过以中文括号开始和结尾的行
        if (isChineseBracketWrapped(optionLine)) {
          currentIndex++;
          continue;
        }
        
        // 如果是分隔符，退出选项收集
        if (optionLine === '---') {
          break;
        }
        
        // 检查是否是表格开始行
        if (isTableRow(optionLine)) {
          const { tableContent, newIndex } = extractTable(currentIndex);
          
          // 如果找到了有效的表格
          if (tableContent && handleTable(tableContent)) {
            // 更新索引到表格后的位置
            currentIndex = newIndex;
            continue;
          }
        }
        
        // 检查是否是选项格式
        if (isOption(optionLine)) {
          // 处理标准格式 "A. 内容"
          let optionMatch = optionLine.match(/^[A-Z]\.\s*(.*)/);
          if (optionMatch) {
            // 只存储选项内容，忽略字母前缀
            addCSVField('选项', optionMatch[1]);
          } else {
            // 处理Markdown格式 "- A. 内容" 或 "   - A. 内容"
            optionMatch = optionLine.match(/^\s*-\s*[A-Z]\.\s*(.*)/);
            if (optionMatch) {
              addCSVField('选项', optionMatch[1]);
            }
          }
          currentIndex++;
        } else {
          break;
        }
      }
    }
    else {
      // 其他内容先检查是否是选项格式
      // 检查是否是选项格式
      if (isOption(line)) {
        // 处理标准格式 "A. 内容"
        let optionMatch = line.match(/^[A-Z]\.\s*(.*)/);
        if (optionMatch) {
          // 只存储选项内容，忽略字母前缀
          addCSVField('选项', optionMatch[1]);
        } else {
          // 处理Markdown格式 "- A. 内容" 或 "   - A. 内容"
          optionMatch = line.match(/^\s*-\s*[A-Z]\.\s*(.*)/);
          if (optionMatch) {
            addCSVField('选项', optionMatch[1]);
          }
        }
      } else {
        // 否则作为题干处理
        // 移除可能存在的题号并添加题干
        addCSVField('题干', removeQuestionNumber(line));
      }
      
      currentIndex++;
      
      // 检查是否有后续选项
      while (currentIndex < lines.length) {
        let nextLine = lines[currentIndex].trim();
        
        // 跳过空行
        if (nextLine === '') {
          currentIndex++;
          continue;
        }
        
        // 跳过以中文括号开始和结尾的行
        if (isChineseBracketWrapped(nextLine)) {
          currentIndex++;
          continue;
        }
        
        // 如果是分隔符，退出
        if (nextLine === '---') {
          break;
        }
        
        // 检查是否是表格开始行
        if (isTableRow(nextLine)) {
          const { tableContent, newIndex } = extractTable(currentIndex);
          
          // 如果找到了有效的表格
          if (tableContent && handleTable(tableContent)) {
            // 更新索引到表格后的位置
            currentIndex = newIndex;
            continue;
          }
        }
        
        // 检查是否是选项格式
        if (isOption(nextLine)) {
          // 处理标准格式 "A. 内容"
          let optionMatch = nextLine.match(/^[A-Z]\.\s*(.*)/);
          if (optionMatch) {
            // 只存储选项内容，忽略字母前缀
            addCSVField('选项', optionMatch[1]);
          } else {
            // 处理Markdown格式 "- A. 内容" 或 "   - A. 内容"
            optionMatch = nextLine.match(/^\s*-\s*[A-Z]\.\s*(.*)/);
            if (optionMatch) {
              addCSVField('选项', optionMatch[1]);
            }
          }
          currentIndex++;
        } else {
          // 如果不是选项格式，作为题干处理
          addCSVField('题干', removeQuestionNumber(nextLine));
          currentIndex++;
        }
      }
    }
  }

  // 将所有CSV字段连接成一个字符串
  return csvFields.join(',');
}

/**
 * 发送转换后的CSV数据到后端
 * @param {string} csvData - 要发送的CSV数据
 * @param {Object} options - 请求选项
 * @returns {Promise} - 返回后端响应的Promise
 */
async function sendCSVToBackend(csvData, options = {}) {
  const { endpoint = '/api/questions/import', method = 'POST' } = options;
  
  try {
    const response = await fetch(endpoint, {
      method,
      headers: {
        'Content-Type': 'text/csv',
        ...options.headers
      },
      body: csvData
    });
    
    if (!response.ok) {
      throw new Error(`服务器返回错误：${response.status} ${response.statusText}`);
    }
    
    return await response.json();
  } catch (error) {
    console.error('发送CSV数据到后端失败:', error);
    throw error;
  }
}

/**
 * 一步完成文本转换和发送到后端
 * @param {string} text - 要转换的文本
 * @param {Object} options - 请求选项
 * @returns {Promise} - 返回后端响应的Promise
 */
async function processAndSendText(text, options = {}) {
  const csvData = convertTextToCSV(text);
  return sendCSVToBackend(csvData, options);
}

// TextConverterService对象
const TextConverterService = {
  convertTextToCSV,
  sendCSVToBackend,
  processAndSendText
};

// 合并UIService中需要的函数
/**
 * 应用暗黑模式
 * @param {boolean} isDarkMode - 是否启用暗黑模式
 */
function applyDarkMode(isDarkMode) {
  if (isDarkMode) {
    // 设置暗色模式
    document.documentElement.setAttribute('theme-mode', 'dark');
    // 更新状态栏颜色为深色
    document.querySelector('meta[name="theme-color"][media="(prefers-color-scheme: dark)"]').setAttribute('content', '#242424');
    document.querySelector('meta[name="theme-color"][media="(prefers-color-scheme: light)"]').setAttribute('content', '#242424');
  } else {
    // 重置为浅色模式
    document.documentElement.removeAttribute('theme-mode');
    // 更新状态栏颜色为浅色
    document.querySelector('meta[name="theme-color"][media="(prefers-color-scheme: dark)"]').setAttribute('content', '#ffffff');
    document.querySelector('meta[name="theme-color"][media="(prefers-color-scheme: light)"]').setAttribute('content', '#ffffff');
  }

  // 保存当前主题模式到localStorage
  localStorage.setItem('theme-mode', isDarkMode ? 'dark' : 'light');
}

/**
 * 初始化主题模式
 * @returns {boolean} 当前是否为暗黑模式
 */
function initThemeMode() {
  // 从localStorage恢复主题模式
  const savedTheme = localStorage.getItem('theme-mode');
  const isDarkMode = savedTheme === 'dark';
  
  // 应用主题设置
  applyDarkMode(isDarkMode);
  
  return isDarkMode;
}

const dbName = 'questionBank'
const configStoreName = 'config'
// 删除旧的存储区常量，改为动态创建
// const questionsStoreName = 'questions'
// const papersStoreName = 'papers'
// 初始版本为1
let currentDBVersion = parseInt(localStorage.getItem('dbVersion') || '1', 10)

// 初始化全局MutationObserver实例
let analysisMutationObserver = null;

// 初始化数据库
const initDB = async () => {
  try {
    // 首先尝试直接打开数据库而不指定版本号，以获取当前的正确版本号
    try {
      // 改用Promise封装，确保可靠获取当前数据库版本
      const getCurrentVersion = () => {
        return new Promise((resolve, reject) => {
          // 避免使用之前可能存在的连接，确保连接断开后再获取
          indexedDB.deleteDatabase = indexedDB.deleteDatabase || window.indexedDB.deleteDatabase;
          const openRequest = indexedDB.open(dbName);
          
          let hasResolved = false;
          
          // 设置更短的超时时间
          const timeoutTimer = setTimeout(() => {
            if (!hasResolved) {
              hasResolved = true;
              console.warn('获取数据库版本超时，使用缓存版本继续');
              reject(new Error('获取数据库版本超时'));
            }
          }, 800); // 减少到800毫秒
          
          openRequest.onsuccess = (event) => {
            clearTimeout(timeoutTimer);
            if (hasResolved) return;
            
            hasResolved = true;
            try {
              const db = event.target.result;
              const version = db.version;
              console.log('获取到数据库当前版本:', version);
              
              // 确保关闭连接
              db.close();
              resolve(version);
            } catch (error) {
              console.error('从打开的数据库获取版本时出错:', error);
              reject(error);
            }
          };
          
          openRequest.onerror = (event) => {
            clearTimeout(timeoutTimer);
            if (hasResolved) return;
            
            hasResolved = true;
            console.error('获取数据库版本出错:', event.target.error);
            reject(event.target.error);
          };
          
          openRequest.onblocked = (event) => {
            // 不清除超时，让超时处理接管
            if (hasResolved) return;
            
            console.warn('获取数据库版本被阻塞，等待超时处理');
          };
        });
      };
      
      // 尝试3次获取版本号，防止偶发错误
      let tryCount = 0;
      let realVersion;
      
      while (tryCount < 3) {
        try {
          // 每次尝试前等待一段时间，避免连续尝试
          if (tryCount > 0) {
            await new Promise(resolve => setTimeout(resolve, 100 * tryCount)); // 累进等待时间
          }
          
          realVersion = await getCurrentVersion();
          // 成功获取到版本号，跳出循环
          currentDBVersion = realVersion;
          localStorage.setItem('dbVersion', currentDBVersion.toString());
          break;
        } catch (err) {
          tryCount++;
          console.warn(`获取数据库版本失败，第${tryCount}次尝试:`, err);
          
          if (tryCount >= 3) {
            console.warn('多次获取数据库版本失败，使用本地缓存版本:', currentDBVersion);
          }
        }
      }
    } catch (preOpenError) {
      console.warn('预检查数据库版本失败:', preOpenError);
    }
    
    // 使用确认后的版本号打开数据库
    console.log(`尝试使用版本 ${currentDBVersion} 打开数据库`);
    
    const db = await openDB(dbName, currentDBVersion, {
      upgrade(db, oldVersion, newVersion, transaction) {
        console.log(`数据库从版本 ${oldVersion} 升级到 ${newVersion}`);
        
        // 确保config存储区存在
        if (!db.objectStoreNames.contains(configStoreName)) {
          console.log('在升级中创建config存储区');
          db.createObjectStore(configStoreName, { keyPath: 'id' });
        }
        
        // 在升级回调中，我们不会创建新的试卷存储区
        // 试卷存储区将在添加试卷时动态创建
      }
    });
    // 成功打开数据库后保存当前版本号
    localStorage.setItem('dbVersion', currentDBVersion.toString());
    return db;
  } catch (error) {
    // 如果是因为尝试打开一个已经被删除的数据库，则尝试创建新数据库
    if (error.name === 'VersionError') {
      console.warn('版本控制错误，尝试使用更高版本号重新打开数据库');
      // 增加版本号并重试
      currentDBVersion += 1;
      // 不立即保存版本号，等成功打开后再保存
      return initDB(); // 递归调用，使用新版本号
    }
    
    console.error('初始化数据库失败:', error);
    throw error;
  }
}

// 辅助函数：安全地从configStore获取数据
const safeGetConfig = async (key, defaultValue = null) => {
  let db = null;
  try {
    // 如果正在连续删除操作中，并且有缓存版本，使用直接打开方式
    if (window.dbOperationState?.processingDelete && window.dbOperationState?.lastKnownVersion) {
      console.log('获取配置时使用缓存版本:', window.dbOperationState.lastKnownVersion);
      db = await openDB(dbName, window.dbOperationState.lastKnownVersion);
      
      if (db.objectStoreNames.contains(configStoreName)) {
        const config = await db.get(configStoreName, key);
        const result = config ? config.data : defaultValue;
        db.close();
        db = null;
        return result;
      }
      db.close();
      db = null;
      return defaultValue;
    }
    
    // 正常初始化流程
    db = await initDB();
    if (db.objectStoreNames.contains(configStoreName)) {
      const config = await db.get(configStoreName, key);
      const result = config ? config.data : defaultValue;
      db.close();
      db = null;
      return result;
    }
    return defaultValue;
  } catch (error) {
    console.warn(`从configStore获取${key}失败:`, error);
    return defaultValue;
  } finally {
    // 确保数据库连接被关闭
    if (db) {
      try {
        db.close();
        console.log(`获取配置[${key}]后关闭数据库连接`);
      } catch (closeError) {
        console.warn('关闭数据库连接失败:', closeError);
      }
    }
  }
};

// 辅助函数：安全地向configStore存储数据
const safePutConfig = async (key, data) => {
  let db = null;
  try {
    // 如果正在进行连续删除操作，使用缓存版本避免重复获取
    if (window.dbOperationState?.processingDelete && window.dbOperationState?.lastKnownVersion) {
      console.log('存储配置时使用缓存数据库版本', window.dbOperationState.lastKnownVersion);
      // 直接使用上次已知版本打开数据库
      db = await openDB(dbName, window.dbOperationState.lastKnownVersion);
      
      if (db.objectStoreNames.contains(configStoreName)) {
        await db.put(configStoreName, { id: key, data });
        db.close();
        db = null;
        return true;
      }
      db.close();
      db = null;
      console.warn(`对象存储${configStoreName}不存在，无法保存${key}`);
      return false;
    }
    
    // 正常流程获取数据库
    db = await initDB();
    if (db.objectStoreNames.contains(configStoreName)) {
      await db.put(configStoreName, { id: key, data });
      db.close();
      db = null;
      return true;
    }
    console.warn(`对象存储${configStoreName}不存在，无法保存${key}`);
    return false;
  } catch (error) {
    console.error(`向configStore存储${key}失败:`, error);
    return false;
  } finally {
    // 确保数据库连接被关闭
    if (db) {
      try {
        db.close();
        console.log(`存储配置[${key}]后关闭数据库连接`);
      } catch (closeError) {
        console.warn('关闭数据库连接失败:', closeError);
      }
    }
  }
}

// 获取试卷存储区名称
const getPaperStoreName = (paperId) => {
  return `paper_${paperId}`
}

// 从数据库加载试卷
const loadPapers = async () => {
  try {
    // 从config表中获取papers映射数据
    const papersMap = await safeGetConfig('papers', {})
    const papersList = []
    
    // 将映射转换为数组
    for (const paperId in papersMap) {
      const paperName = papersMap[paperId]
      // 验证对应的存储区是否存在
      const db = await initDB()
      if (db.objectStoreNames.contains(getPaperStoreName(paperId))) {
        papersList.push({
          id: paperId,
          name: paperName
        })
      }
    }
    
    // 按ID降序排序，最新的在前面
    return papersList.sort((a, b) => b.id - a.id)
  } catch (error) {
    console.error('加载试卷列表失败:', error)
    return []
  }
}

// 添加新试卷
const addPaper = async (name) => {
  try {
    // 1. 生成唯一的试卷ID
    const paperId = Date.now().toString() // 使用时间戳作为ID
    console.log(`开始创建试卷: ${name}, ID: ${paperId}`)
    
    // 2. 确保关闭所有现有的数据库连接
    try {
      const connections = await window.indexedDB.databases();
      for (const connection of connections) {
        if (connection.name === dbName) {
          // 手动打开然后关闭连接，确保彻底关闭
          const closeRequest = indexedDB.open(dbName);
          await new Promise(resolve => {
            closeRequest.onsuccess = function(event) {
              const db = event.target.result;
              db.close();
              console.log('添加试卷前关闭现有数据库连接');
              resolve();
            };
            closeRequest.onerror = function() {
              console.warn('关闭数据库连接失败');
              resolve(); // 即使失败也继续执行
            };
          });
        }
      }
    } catch (closeError) {
      console.warn('关闭数据库连接时出错，将继续尝试创建试卷:', closeError);
    }
    
    // 3. 获取当前数据库的最新版本号
    let actualVersion = currentDBVersion;
    try {
      const getVersionRequest = indexedDB.open(dbName);
      await new Promise((resolve, reject) => {
        let handled = false;
        
        getVersionRequest.onsuccess = function(event) {
          if (handled) return;
          handled = true;
          
          const db = event.target.result;
          actualVersion = db.version;
          console.log('添加试卷前获取实际数据库版本:', actualVersion);
          db.close();
          resolve();
        };
        
        getVersionRequest.onerror = function(event) {
          if (handled) return;
          handled = true;
          
          console.error('获取数据库版本失败:', event.target.error);
          reject(event.target.error);
        };
        
        // 设置超时保护
        setTimeout(() => {
          if (!handled) {
            handled = true;
            console.warn('获取数据库版本超时');
            reject(new Error('获取数据库版本超时'));
          }
        }, 2000);
      });
      
      // 获取成功，更新本地版本号
      currentDBVersion = actualVersion;
      localStorage.setItem('dbVersion', currentDBVersion.toString());
    } catch (versionError) {
      console.warn('获取数据库版本失败，使用缓存版本:', currentDBVersion);
    }
    
    // 4. 检查并确保config存储区存在
    try {
      // 打开数据库，检查config存储区是否存在
      let db = await openDB(dbName, currentDBVersion);
      
      let configExists = db.objectStoreNames.contains(configStoreName);
      // 关闭连接
      db.close();
      
      // 如果config不存在，则创建它
      if (!configExists) {
        // 如果不存在，则增加版本号并创建config存储区
        currentDBVersion += 1;
        console.log(`创建config存储区，版本升级到 ${currentDBVersion}`);
        localStorage.setItem('dbVersion', currentDBVersion.toString());
        
        await openDB(dbName, currentDBVersion, {
          upgrade(db, oldVersion, newVersion, transaction) {
            console.log(`升级过程：创建config存储区，版本从 ${oldVersion} 升级到 ${newVersion}`);
            // 创建config存储区
            db.createObjectStore(configStoreName, { keyPath: 'id' });
          }
        }).then(db => db.close());
      }
      
      // 5. 先创建新试卷的存储区，再更新papers映射
      try {
        // 再次获取当前数据库的实际版本号，防止在添加题目后版本不一致
        const freshVersionRequest = indexedDB.open(dbName);
        const actualDBVersion = await new Promise((resolve, reject) => {
          let handled = false;
          
          freshVersionRequest.onsuccess = function(event) {
            if (handled) return;
            handled = true;
            
            const db = event.target.result;
            const version = db.version;
            console.log('添加试卷前再次获取实际数据库版本:', version);
            db.close();
            resolve(version);
          };
          
          freshVersionRequest.onerror = function(event) {
            if (handled) return;
            handled = true;
            
            console.error('获取数据库版本失败:', event.target.error);
            reject(event.target.error);
          };
          
          // 设置超时保护
          setTimeout(() => {
            if (!handled) {
              handled = true;
              console.warn('获取数据库版本超时');
              reject(new Error('获取数据库版本超时'));
            }
          }, 2000);
        });
        
        // 确保使用最新的版本号
        currentDBVersion = actualDBVersion;
        localStorage.setItem('dbVersion', currentDBVersion.toString());
      } catch (freshVersionError) {
        console.warn('获取最新版本失败，使用当前缓存版本:', currentDBVersion);
      }
      
      const createVersion = currentDBVersion + 1;
      console.log(`创建试卷存储区，版本升级到 ${createVersion}`);
      localStorage.setItem('dbVersion', createVersion.toString());
      
      await openDB(dbName, createVersion, {
        upgrade(db, oldVersion, newVersion, transaction) {
          console.log(`升级过程：为新试卷创建存储区，版本从 ${oldVersion} 升级到 ${newVersion}`);
          
          // 确保config存储区存在（以防万一）
          if (!db.objectStoreNames.contains(configStoreName)) {
            db.createObjectStore(configStoreName, { keyPath: 'id' });
          }
          
          // 为新试卷创建对象存储区
          const paperStoreName = getPaperStoreName(paperId);
          if (!db.objectStoreNames.contains(paperStoreName)) {
            db.createObjectStore(paperStoreName, { keyPath: 'id', autoIncrement: true });
          }
        }
      }).then(db => db.close());
      
      // 更新当前版本号
      currentDBVersion = createVersion;
      localStorage.setItem('dbVersion', currentDBVersion.toString());
      
      // 6. 现在存储区已创建，再更新papers映射
      const configDb = await openDB(dbName, currentDBVersion);
      
      // 尝试获取papers映射
      let papersMap = {};
      const papersEntry = await configDb.get(configStoreName, 'papers').catch(error => {
        console.warn('无法获取papers映射，使用空对象代替', error);
        return { data: {} };
      });
      
      papersMap = papersEntry?.data || {};
      
      // 更新papers映射
      papersMap[paperId] = name;
      
      // 保存更新后的papers映射
      await configDb.put(configStoreName, { id: 'papers', data: papersMap }).catch(error => {
        console.error('保存papers映射失败', error);
        throw error;
      });
      
      console.log('已更新papers映射', papersMap);
      
      // 初始化试卷的最后修改时间
      try {
        // 获取现有的更新时间映射
        const updateTimesEntry = await configDb.get(configStoreName, 'papers_update_time');
        const updateTimesMap = updateTimesEntry?.data || {};
        
        // 设置新试卷的创建时间
        updateTimesMap[paperId] = Date.now();
        
        // 保存回数据库
        await configDb.put(configStoreName, { id: 'papers_update_time', data: updateTimesMap });
        console.log('已初始化试卷的最后修改时间');
      } catch (error) {
        console.error('初始化试卷最后修改时间失败', error);
        // 继续执行，不中断流程
      }
      
      // 关闭连接
      configDb.close();
      
    } catch (error) {
      console.error('数据库操作失败', error);
      if (error.name === 'VersionError') {
        // 如果是版本错误，尝试递增版本再试
        currentDBVersion += 1;
        console.warn(`版本错误，尝试增加版本号到 ${currentDBVersion} 后重试`);
        localStorage.setItem('dbVersion', currentDBVersion.toString());
        return addPaper(name); // 递归重试，但仅限于版本错误
      }
      throw error;
    }
    
    // 创建试卷对象并返回
    const paper = {
      id: paperId,
      name
    };
    
    // 更新本地状态
    papers.value.push(paper);
    console.log(`试卷 "${name}" 创建成功，ID: ${paperId}`);
    
    // 如果启用了同步，则立即将试卷信息同步到云端的config.json
    if (syncConfig.value.syncEnabled) {
      try {
        console.log('立即将新试卷信息同步到云端...');
        
        // 初始化S3客户端
        const s3Client = new S3Client({
          region: syncConfig.value.s3Region,
          endpoint: cleanEndpointUrl(syncConfig.value.s3Endpoint),
          credentials: {
            accessKeyId: syncConfig.value.s3AccessKeyId,
            secretAccessKey: syncConfig.value.s3SecretAccessKey
          },
          forcePathStyle: true
        });
        
        // 先尝试获取云端的config.json
        let cloudConfig = null;
        
        try {
          // 检查config.json是否存在
          const listCommand = new ListObjectsV2Command({
            Bucket: syncConfig.value.s3Bucket,
            Prefix: 'config.json',
            MaxKeys: 1
          });
          
          const listResponse = await s3Client.send(listCommand);
          
          // 如果找到config.json，下载它
          if (listResponse.Contents && listResponse.Contents.length > 0 && 
              listResponse.Contents[0].Key === 'config.json') {
            
            const getConfigCommand = new GetObjectCommand({
              Bucket: syncConfig.value.s3Bucket,
              Key: 'config.json'
            });
            
            const configResponse = await s3Client.send(getConfigCommand);
            const configBuffer = await configResponse.Body.transformToString();
            cloudConfig = JSON.parse(configBuffer);
          }
        } catch (configError) {
          console.warn('获取云端config.json失败，将创建新的config.json:', configError);
        }
        
        // 如果没有获取到云端config，则创建新的
        if (!cloudConfig) {
          cloudConfig = {
            timestamp: Date.now(),
            service_config: await safeGetConfig('service_config', defaultConfig),
            papers: await safeGetConfig('papers', {}),
            papers_update_time: await safeGetConfig('papers_update_time', {})
          };
        } else {
          // 如果获取到了云端config，更新它的内容
          // 使用本地最新的数据
          cloudConfig.timestamp = Date.now();
          cloudConfig.service_config = await safeGetConfig('service_config', defaultConfig);
          cloudConfig.papers = await safeGetConfig('papers', {});
          cloudConfig.papers_update_time = await safeGetConfig('papers_update_time', {});
        }
        
        // 确保试卷信息在云端config中
        if (!cloudConfig.papers) cloudConfig.papers = {};
        if (!cloudConfig.papers_update_time) cloudConfig.papers_update_time = {};
        
        cloudConfig.papers[paperId] = name;
        cloudConfig.papers_update_time[paperId] = Date.now();
        
        // 上传更新后的config.json
        const configUploadParams = {
          Bucket: syncConfig.value.s3Bucket,
          Key: 'config.json',
          Body: JSON.stringify(cloudConfig),
          ContentType: 'application/json'
        };
        
        const configCommand = new PutObjectCommand(configUploadParams);
        await s3Client.send(configCommand);
        
        // 创建并上传新试卷的json文件
        const paperBackup = {
          timestamp: Date.now(),
          paper: {
            id: paperId,
            name
          },
          questions: [] // 新试卷，没有题目
        };
        
        const paperFileName = `paper_${paperId}.json`;
        const paperUploadParams = {
          Bucket: syncConfig.value.s3Bucket,
          Key: paperFileName,
          Body: JSON.stringify(paperBackup),
          ContentType: 'application/json'
        };
        
        const paperCommand = new PutObjectCommand(paperUploadParams);
        await s3Client.send(paperCommand);
        
        console.log('新试卷信息已成功同步到云端');
        
        // 重置同步标志，因为该试卷已同步
        hasPendingSync.value = false;
      } catch (syncError) {
        console.error('同步新试卷信息到云端失败:', syncError);
        // 如果同步失败，回归到延迟同步机制
        scheduleDelayedSync();
      }
    }
    
    return paper;
  } catch (error) {
    console.error('添加试卷失败:', error);
    // 显示错误通知给用户
    NotifyPlugin.error({
      title: '添加试卷失败',
      content: error.message,
      duration: 3000,
      placement: 'top-left'
    });
    throw error;
  }
}

// 保存试题到数据库
const saveQuestion = async (question, paperId) => {
  let db = null;
  try {
    if (!paperId) {
      throw new Error('未指定试卷ID')
    }
    
    // 获取试卷对应的存储区名称
    const paperStoreName = getPaperStoreName(paperId)
    
    // 连接数据库
    db = await initDB()
    
    // 验证存储区是否存在
    if (!db.objectStoreNames.contains(paperStoreName)) {
      throw new Error(`试卷存储区 ${paperStoreName} 不存在`)
    }
    
    // 将题目保存到试卷存储区
    const questionId = await db.add(paperStoreName, {
      detail: question.detail,
      answer: "",
      simpleAnswer: "",
      keywords: [],
      stars: 0
    })
    
    // 更新试卷的最后修改时间
    await updatePaperLastModifiedTime(paperId)
    
    return questionId
  } catch (error) {
    console.error('保存试题失败:', error)
    throw error
  } finally {
    // 确保数据库连接被关闭
    if (db) {
      try {
        db.close();
        console.log('保存题目后关闭数据库连接');
      } catch (closeError) {
        console.warn('关闭数据库连接失败:', closeError);
      }
    }
  }
}

// 从数据库加载试题
const loadQuestions = async (paperId) => {
  let db = null;
  try {
    if (!paperId) {
      return []
    }
    
    // 获取试卷对应的存储区名称
    const paperStoreName = getPaperStoreName(paperId)
    
    // 连接数据库
    db = await initDB()
    
    // 验证存储区是否存在
    if (!db.objectStoreNames.contains(paperStoreName)) {
      console.warn(`试卷存储区 ${paperStoreName} 不存在`)
      return []
    }
    
    // 不再需要从config表中加载星级评分数据，star直接保存在题目中
    
    // 从试卷存储区加载所有题目
    const questions = await db.getAll(paperStoreName)
    
    // 提前关闭数据库连接
    if (db) {
      try {
        db.close();
        console.log('加载题目后关闭数据库连接');
      } catch (closeError) {
        console.warn('关闭数据库连接失败:', closeError);
      }
      db = null;
    }
    
    // 处理每个题目，确保格式一致性
    return questions.map(question => {
      // 确保题目是新格式（detail数组），如果不是则转换
      const convertedQuestion = convertToDetailFormat(question)
      
      // 创建处理后的题目对象
      const processedQuestion = { 
        ...convertedQuestion, 
        id: question.id,
        showAnalysis: false,
        // 直接使用题目中保存的星级，如果没有则默认为0
        stars: question.stars || 0
      }
      
      // 确保answer是字符串格式
      if (Array.isArray(processedQuestion.answer)) {
        processedQuestion.answer = processedQuestion.answer.length > 0 ? 
          processedQuestion.answer.join('\n') : ''
      } else if (processedQuestion.answer === null || processedQuestion.answer === undefined) {
        processedQuestion.answer = ''
      }
      
      // 确保simpleAnswer是字符串格式
      if (Array.isArray(processedQuestion.simpleAnswer)) {
        processedQuestion.simpleAnswer = processedQuestion.simpleAnswer.length > 0 ? 
          processedQuestion.simpleAnswer.join('\n') : ''
      } else if (processedQuestion.simpleAnswer === null || processedQuestion.simpleAnswer === undefined) {
        processedQuestion.simpleAnswer = ''
      }
      
      return processedQuestion
    })
  } catch (error) {
    console.error('加载试题失败:', error)
    return []
  } finally {
    // 确保数据库连接被关闭
    if (db) {
      try {
        db.close();
        console.log('加载题目异常后关闭数据库连接');
      } catch (closeError) {
        console.warn('关闭数据库连接失败:', closeError);
      }
    }
  }
}

// 修改saveConfig函数以适应新的数据结构
const saveConfig = async () => {
  try {
    if (activeTab.value === 'add') {
      const questions = parseQuestion(newQuestion.value)
      
      if (currentPaper.value && currentPaper.value.id) {
        // 批量保存题目到对应的试卷存储区
        for (const question of questions) {
          await saveQuestion(question, currentPaper.value.id)
        }
        
        // 重新加载当前试卷的题目
        currentQuestions.value = await loadQuestions(currentPaper.value.id)
      }
    }
    
    // 无论成功或失败，关闭所有活跃的数据库连接
    try {
      const connections = await window.indexedDB.databases();
      let connectionsClosed = 0;
      
      for (const connection of connections) {
        if (connection.name === dbName) {
          // 手动打开然后关闭连接，确保彻底关闭
          const closeRequest = indexedDB.open(dbName);
          await new Promise(resolve => {
            closeRequest.onsuccess = function(event) {
              const db = event.target.result;
              db.close();
              connectionsClosed++;
              console.log(`添加题目后关闭数据库连接 ${connectionsClosed}`);
              resolve();
            };
            closeRequest.onerror = function() {
              console.warn('关闭数据库连接失败');
              resolve(); // 即使失败也继续执行
            };
          });
        }
      }
      
      // 如果关闭了连接，等待短暂时间让连接完全释放
      if (connectionsClosed > 0) {
        await new Promise(resolve => setTimeout(resolve, 100));
      }
    } catch (closeError) {
      console.warn('关闭数据库连接出错:', closeError);
    }
    
    drawerVisible.value = false
    newQuestion.value = ''
    parseLastLineAsAnswer.value = false // 重置复选框状态
  } catch (error) {
    console.error('保存题目时出错:', error);
    
    // 出错时也尝试关闭连接
    try {
      const connections = await window.indexedDB.databases();
      for (const connection of connections) {
        if (connection.name === dbName) {
          const closeRequest = indexedDB.open(dbName);
          await new Promise(resolve => {
            closeRequest.onsuccess = function(event) {
              const db = event.target.result;
              db.close();
              resolve();
            };
            closeRequest.onerror = function() {
              resolve();
            };
          });
        }
      }
    } catch (e) {
      console.warn('出错后关闭连接失败:', e);
    }
    
    // 显示错误通知
    NotifyPlugin.error({
      title: '保存题目失败',
      content: error.message,
      duration: 3000,
      placement: 'top-left'
    });
  }
}

// 修改handleDeletePaper函数以适应新的数据结构
const handleDeletePaper = async () => {
  if (!paperToDelete.value) {
    console.warn('没有选中要删除的试卷')
    deleteDialogVisible.value = false
    return
  }

  // 初始化全局状态（如果未初始化）
  window.dbOperationState = window.dbOperationState || {
    lastOperationTime: 0,
    consecutiveOperations: 0,
    operationType: null,
    skipVersionCheck: false,
    processingDelete: false,
    lastKnownVersion: null
  };
  
  // 保存要删除的试卷信息到临时变量
  const paperToDeleteCopy = { ...paperToDelete.value }
  
  // 立即关闭对话框，避免用户等待
  deleteDialogVisible.value = false
  
  // 检查是否为1秒内的连续删除操作
  const isConsecutiveDelete = (Date.now() - window.dbOperationState.lastOperationTime < 1000) && 
                              window.dbOperationState.operationType === 'delete';
  
  // 更新全局操作状态
  window.dbOperationState.lastOperationTime = Date.now();
  window.dbOperationState.operationType = 'delete';
  
  if (isConsecutiveDelete) {
    window.dbOperationState.consecutiveOperations++;
    // 如果是连续第3次或以上操作，标记跳过版本检查
    if (window.dbOperationState.consecutiveOperations >= 2) {
      window.dbOperationState.skipVersionCheck = true;
    }
  } else {
    window.dbOperationState.consecutiveOperations = 0;
    window.dbOperationState.skipVersionCheck = false;
  }
  
  // 标记开始处理删除操作
  window.dbOperationState.processingDelete = true;
  
  // 如果启用了同步，先执行一次同步确保数据同步
  if (syncConfig.value.syncEnabled) {
    try {
      // 立即完成计划中的双向同步（如果有）
      if (syncDebounceTimer.value) {
        clearTimeout(syncDebounceTimer.value);
        syncDebounceTimer.value = null;
        console.log('删除试卷前，取消了等待中的同步任务');
        
        // 执行一次同步
        await silentSync();
        console.log('删除试卷前，完成了同步操作');
      }
    } catch (syncError) {
      console.error('删除试卷前同步失败:', syncError);
      // 即使同步失败也继续删除操作
    }
  }
  
  // 在后台异步处理删除操作
  setTimeout(async () => {
    try {
      // 1. 先确保关闭所有现有的数据库连接
      try {
        const connections = await window.indexedDB.databases();
        let connectionsClosed = 0;
        
        for (const connection of connections) {
          if (connection.name === dbName) {
            // 手动打开然后关闭连接，确保彻底关闭
            const closeRequest = indexedDB.open(dbName);
            await new Promise(resolve => {
              closeRequest.onsuccess = function(event) {
                const db = event.target.result;
                db.close();
                connectionsClosed++;
                console.log(`已关闭现有数据库连接 ${connectionsClosed}`);
                resolve();
              };
              closeRequest.onerror = function() {
                console.warn('关闭数据库连接失败');
                resolve(); // 即使失败也继续执行
              };
            });
          }
        }
        
        // 如果关闭了连接，等待短暂时间让连接完全释放
        if (connectionsClosed > 0) {
          await new Promise(resolve => setTimeout(resolve, 50));
        }
      } catch (closeError) {
        console.warn('关闭数据库连接出错，但将继续尝试删除:', closeError);
      }
      
      // 2. 获取当前数据库的最新版本号，但跳过连续删除操作
      let deleteVersion;
      
      if (window.dbOperationState.skipVersionCheck && window.dbOperationState.lastKnownVersion) {
        // 连续删除，直接使用已知版本+1，避免频繁查询导致阻塞
        deleteVersion = window.dbOperationState.lastKnownVersion + 1;
        console.log(`连续删除操作(${window.dbOperationState.consecutiveOperations})，跳过版本查询，使用版本:`, deleteVersion);
      } else {
        let latestVersion = currentDBVersion;
        try {
          const getVersionRequest = indexedDB.open(dbName);
          await new Promise((resolve, reject) => {
            let handled = false;
            
            // 减少超时时间，避免长时间等待
            const timeoutTimer = setTimeout(() => {
              if (!handled) {
                handled = true;
                console.warn('获取数据库版本超时，使用本地版本号+1');
                reject(new Error('获取数据库版本超时'));
              }
            }, 800); // 减少到800毫秒
            
            getVersionRequest.onsuccess = function(event) {
              clearTimeout(timeoutTimer);
              if (handled) return;
              handled = true;
              
              const db = event.target.result;
              latestVersion = db.version;
              console.log('删除前获取到的最新数据库版本:', latestVersion);
              db.close();
              resolve();
            };
            
            getVersionRequest.onerror = function(event) {
              clearTimeout(timeoutTimer);
              if (handled) return;
              handled = true;
              
              console.error('获取数据库版本失败:', event.target.error);
              reject(event.target.error);
            };
          });
          
          // 获取成功，更新本地版本号
          currentDBVersion = latestVersion;
          localStorage.setItem('dbVersion', currentDBVersion.toString());
        } catch (versionError) {
          console.warn('获取版本号失败，使用当前缓存版本继续:', currentDBVersion);
        }
        
        deleteVersion = currentDBVersion + 1;
      }
      
      // 更新全局已知版本号
      window.dbOperationState.lastKnownVersion = deleteVersion;
      
      // 3. 使用确定的版本号来进行删除操作
      console.log(`准备删除试卷存储区，尝试升级数据库到版本 ${deleteVersion}`);
      
      const newDb = await openDB(dbName, deleteVersion, {
        upgrade(db, oldVersion, newVersion, transaction) {
          console.log(`删除试卷存储区，版本从 ${oldVersion} 升级到 ${newVersion}`);
          
          // 删除对应的试卷存储区
          const paperStoreName = getPaperStoreName(paperToDeleteCopy.id);
          if (db.objectStoreNames.contains(paperStoreName)) {
            db.deleteObjectStore(paperStoreName);
          }
        }
      });
      
      // 确保删除操作完成后关闭数据库，以避免连接冲突
      newDb.close();
      
      // 更新当前版本号
      currentDBVersion = deleteVersion;
      localStorage.setItem('dbVersion', currentDBVersion.toString());
      
      // 4. 从配置中删除试卷信息
      const papersMap = await safeGetConfig('papers', {});
      delete papersMap[paperToDeleteCopy.id];
      await safePutConfig('papers', papersMap);
      
      // 5. 从配置中删除试卷的更新时间记录
      const updateTimesMap = await safeGetConfig('papers_update_time', {});
      delete updateTimesMap[paperToDeleteCopy.id];
      await safePutConfig('papers_update_time', updateTimesMap);
      
      // 6. 如果启用了云同步，执行云端删除操作
      if (syncConfig.value.syncEnabled) {
        try {
          console.log('开始删除云端试卷数据:', paperToDeleteCopy.id);
          
          // 初始化S3客户端
          const s3Client = new S3Client({
            region: syncConfig.value.s3Region,
            endpoint: cleanEndpointUrl(syncConfig.value.s3Endpoint),
            credentials: {
              accessKeyId: syncConfig.value.s3AccessKeyId,
              secretAccessKey: syncConfig.value.s3SecretAccessKey
            },
            forcePathStyle: true
          });
          
          // 删除试卷json文件
          const paperFileName = `paper_${paperToDeleteCopy.id}.json`;
          const deleteCommand = new DeleteObjectCommand({
            Bucket: syncConfig.value.s3Bucket,
            Key: paperFileName
          });
          
          await s3Client.send(deleteCommand);
          console.log('已从云端删除试卷JSON文件:', paperFileName);
          
          // 上传更新后的config.json到云端
          // 获取本地试卷映射和服务配置
          const updatedPapersMap = await safeGetConfig('papers', {});
          const updatedUpdateTimesMap = await safeGetConfig('papers_update_time', {});
          const serviceConfigData = await safeGetConfig('service_config', defaultConfig);
          
          const finalConfigBackup = {
            timestamp: Date.now(),
            service_config: serviceConfigData,
            papers: updatedPapersMap,
            papers_update_time: updatedUpdateTimesMap
          };
          
          const configUploadParams = {
            Bucket: syncConfig.value.s3Bucket,
            Key: 'config.json',
            Body: JSON.stringify(finalConfigBackup),
            ContentType: 'application/json'
          };
          
          const configCommand = new PutObjectCommand(configUploadParams);
          await s3Client.send(configCommand);
          console.log('已更新云端config.json，删除了试卷记录');
          
        } catch (cloudDeleteError) {
          console.error('删除云端试卷数据时出错:', cloudDeleteError);
          // 即使云端删除失败，本地删除也已完成，不中断流程
        }
      }
      
      // 7. 更新本地状态
      const paperIndex = papers.value.findIndex(p => p.id === paperToDeleteCopy.id);
      if (paperIndex !== -1) {
        papers.value.splice(paperIndex, 1);
      }
      
      // 8. 如果删除的是当前显示的试卷，切换到其他试卷
      if (currentPaper.value && currentPaper.value.id === paperToDeleteCopy.id) {
        if (papers.value.length > 0) {
          currentPaper.value = papers.value[0];
          currentQuestions.value = await loadQuestions(currentPaper.value.id);
          // 更新保存的试卷ID
          localStorage.setItem(CURRENT_PAPER_KEY, currentPaper.value.id);
        } else {
          currentPaper.value = null;
          currentQuestions.value = [];
          localStorage.removeItem(CURRENT_PAPER_KEY);
        }
      }
      
      // 9. 显示成功通知
      NotifyPlugin.success({
        title: '删除成功',
        content: `试卷"${paperToDeleteCopy.name}"已删除${syncConfig.value.syncEnabled ? '(本地和云端)' : ''}`,
        placement: 'top-left',
        duration: 1000
      });
      
      paperToDelete.value = null;
    } catch (error) {
      console.error('删除试卷时出错:', error);
      
      // 显示错误通知
      NotifyPlugin.error({
        title: '删除失败',
        content: error.message,
        placement: 'top-left',
        duration: 3000
      });
      
      paperToDelete.value = null;
    } finally {
      // 操作完成，重置删除处理状态
      window.dbOperationState.processingDelete = false;
    }
  }, 0);
}

const showConfigDrawer = async () => {
  drawerVisible.value = true
  selectedPaper.value = currentPaper.value || 'new'
  
  // 关闭所有数据库连接
  try {
    const connections = await window.indexedDB.databases()
    let connectionsClosed = 0
    
    for (const connection of connections) {
      if (connection.name === dbName) {
        // 手动打开然后关闭连接，确保彻底关闭
        const closeRequest = indexedDB.open(dbName)
        await new Promise(resolve => {
          closeRequest.onsuccess = function(event) {
            const db = event.target.result
            db.close()
            connectionsClosed++
            console.log(`打开设置抽屉时，已关闭数据库连接 ${connectionsClosed}`)
            resolve()
          }
          closeRequest.onerror = function() {
            console.warn('关闭数据库连接失败')
            resolve() // 即使失败也继续执行
          }
        })
      }
    }
    
    if (connectionsClosed > 0) {
      console.log(`共关闭了 ${connectionsClosed} 个数据库连接`)
    } else {
      console.log('没有发现需要关闭的数据库连接')
    }
  } catch (error) {
    console.error('关闭数据库连接时出错:', error)
  }
}

// 初始化应用数据函数
const initializeApp = async () => {
  console.log('开始初始化应用数据...')
  
  try {
    // 首先确保config存储区存在
    let db = await initDB()
    if (!db.objectStoreNames.contains(configStoreName)) {
      console.log('初始化时config存储区不存在，创建它')
      db.close()
      // 增加版本号并创建config存储区
      currentDBVersion += 1
      db = await openDB(dbName, currentDBVersion, {
        upgrade(db, oldVersion, newVersion, transaction) {
          console.log(`初始化创建config存储区，版本从 ${oldVersion} 升级到 ${newVersion}`)
          if (!db.objectStoreNames.contains(configStoreName)) {
            db.createObjectStore(configStoreName, { keyPath: 'id' })
          }
        }
      })
      // 关闭创建后的连接
      db.close()
    }
    
    // 1. 先加载同步配置，因为需要知道是否启用了同步
    loadSyncConfig()
    
    // 2. 加载服务配置（Siliconflow服务）
    await loadServiceConfig()
    
    // 设置papers_update_time更改监听
    setupUpdateTimeChangeMonitor()
    
    // 如果同步已启用，在页面加载时执行一次静默同步
    if (syncConfig.value.syncEnabled) {
      console.log('检测到同步已启用，将在应用初始化后执行一次初始同步');
      
      // 等待应用初始化完成后再执行同步，使用稍长的延迟以确保数据已完全加载
      // 设置一个标记，指示这是应用初始化时的同步，不是由数据变更触发的
      setTimeout(() => {
        // 直接执行silentSync，而不是通过scheduleDelayedSync触发
        // 这样避免了在初始化阶段触发监听器导致的不必要额外同步
        silentSync().then(() => {
          console.log('应用初始化后的同步已完成');
        }).catch(error => {
          console.error('应用初始化后的同步失败:', error);
        });
      }, 3000); // 延长至3秒，确保应用已完全初始化
    }
    
    // 加载试卷列表
    papers.value = await loadPapers()
    
    // 恢复之前选择的试卷
    const savedPaperId = localStorage.getItem(CURRENT_PAPER_KEY)
    if (savedPaperId && papers.value.length > 0) {
      const savedPaper = papers.value.find(p => p.id === savedPaperId)
      if (savedPaper) {
        currentPaper.value = savedPaper
        console.log(`恢复之前选择的试卷: ${savedPaper.name}`)
        // 加载试卷的题目
        currentQuestions.value = await loadQuestions(savedPaper.id)
      } else {
        // 如果找不到保存的试卷，使用第一个
        currentPaper.value = papers.value[0]
        console.log(`找不到之前的试卷，使用第一个: ${papers.value[0].name}`)
        // 加载试卷的题目
        currentQuestions.value = await loadQuestions(papers.value[0].id)
      }
    }
  } catch (error) {
    console.error('初始化应用数据失败:', error)
    // 显示错误通知给用户
    NotifyPlugin.error({
      title: '初始化失败',
      content: '应用数据初始化失败，请尝试刷新页面',
      duration: 0,
      placement: 'top-left'
    })
  }
}

const drawerVisible = ref(false)
const activeTab = ref('add')
const currentPaper = ref('')
const papers = ref([])
const newQuestion = ref('')
const parseLastLineAsAnswer = ref(false) // 是否将每道题的最后一行作为答案
const selectedPaper = ref('')
const contentListRef = ref(null)
const windowWidth = ref(window.innerWidth)
const isAddingPaper = ref(false)
const newPaperName = ref('')
const deleteDialogVisible = ref(false)
const paperToDelete = ref(null)
const renameDialogVisible = ref(false)
const paperToRename = ref(null)
const editQuestionDialogVisible = ref(false)
const deleteQuestionDialogVisible = ref(false)
const editingQuestion = ref(null)
const editingQuestionContent = ref('')
// AI解析进度追踪
const aiAnalysisProgress = ref({
  total: 0,
  processed: 0,
  inProgress: false
})

const currentQuestions = ref([])

// 暗黑模式状态
const isDarkMode = ref(false)

const stickyToolRef = ref(null)
let isDragging = false
let startX = 0
let startY = 0
let currentX = 0
let currentY = 0

const STORAGE_KEY = 'serviceConfig'
const TOOLBAR_POSITION_KEY = 'toolbarPosition'
const SCROLL_POSITION_KEY = 'scrollPosition'
const CURRENT_PAPER_KEY = 'currentPaper'
const SYNC_CONFIG_KEY = 'syncConfig'
const defaultConfig = {
  token: '',
  model: '',
  prompt: '请解答以下单项选择题（组），不要包含选项前的字母。简洁解析中，请包含这个题目的必要内容和所选选项的文字内容，并以自然语言逻辑简洁地解析选择的选项，不要解释没有选择的选项。详细解析中，请解释相关考点、易错项、未选的选项成立的情况，要求简明扼要，纯文本的形式不要用markdown的形式：'
}
const serviceConfig = ref({ ...defaultConfig })
const syncConfig = ref({
  syncEnabled: localStorage.getItem('syncEnabled') === 'true' || false,
  s3Endpoint: localStorage.getItem('s3Endpoint') || '',
  s3Region: localStorage.getItem('s3Region') || 'auto',
  s3AccessKeyId: localStorage.getItem('s3AccessKeyId') || '',
  s3SecretAccessKey: localStorage.getItem('s3SecretAccessKey') || '',
  s3Bucket: localStorage.getItem('s3Bucket') || '',
  enabledServices: ['默认后端服务']  // 默认启用后端服务
})

// 计算抽屉宽度
const drawerSize = computed(() => {
  return windowWidth.value < 700 ? '100vw' : '700px'
})

// 监听窗口大小变化
const handleResize = () => {
  windowWidth.value = window.innerWidth
  checkToolbarPosition()
}

// 检查工具栏位置是否在可视区域内
const checkToolbarPosition = () => {
  const tool = stickyToolRef.value?.$el
  if (!tool) return

  const rect = tool.getBoundingClientRect()
  const windowWidth = window.innerWidth
  const windowHeight = window.innerHeight

  // 检查工具栏是否完全在可视区域内
  const isFullyVisible = 
    rect.left >= 0 &&
    rect.top >= 0 &&
    rect.right <= windowWidth &&
    rect.bottom <= windowHeight

  // 如果工具栏不在可视区域内，重置位置
  if (!isFullyVisible) {
    tool.style.position = 'fixed'
    tool.style.right = '24px'
    tool.style.bottom = '24px'
    tool.style.left = 'auto'
    tool.style.top = 'auto'
    
    // 保存重置后的位置
    localStorage.removeItem(TOOLBAR_POSITION_KEY)
    console.log('工具栏位置已重置，因为窗口大小改变导致其超出可视区域')
  }
}

// 解析试题内容
const parseQuestion = (content) => {
  if (!content) return []
  
  // 首先按照分隔符"---"将内容分割成多个题目部分
  const questionParts = content.split(/\n\s*---\s*\n/)
  const questions = []
  
  // 处理每个题目部分
  for (const questionPart of questionParts) {
    if (!questionPart.trim()) continue
    
    // 如果启用了"将最后一行作为答案"选项，则处理最后一行
    let questionText = questionPart
    let lastLineAnswer = ''
    
    if (parseLastLineAsAnswer.value) {
      // 按换行符分割
      const lines = questionPart.split(/\r?\n/)
      const nonEmptyLines = lines.filter(line => line.trim() !== '')
      
      if (nonEmptyLines.length > 1) {
        // 提取最后一行作为答案
        lastLineAnswer = nonEmptyLines.pop().trim()
        // 重新组合剩余行作为题目内容
        questionText = nonEmptyLines.join('\n')
      }
    }
    
    // 使用TextConverterService处理文本
    const csvData = TextConverterService.convertTextToCSV(questionText)
    
    // 解析CSV字符串
    const parts = parseCSVString(csvData)
    
    // 创建题目对象
    const question = {
      detail: [], 
      answer: "", 
      simpleAnswer: "",
      keywords: [],
      stars: 0
    }
    
    // 提取所有内容到detail列表
    for (let i = 0; i < parts.length; i += 2) {
      if (i + 1 >= parts.length) break
      
      const type = parts[i]
      const content = parts[i + 1]
      
      // 将所有内容按原格式添加到detail中
      question.detail.push(type)
      question.detail.push(content)
    }
    
    // 如果启用了"将最后一行作为答案"选项，且有答案内容，则添加到detail
    if (parseLastLineAsAnswer.value && lastLineAnswer) {
      question.detail.push('答案')
      question.detail.push(lastLineAnswer)
    }
    
    // 如果detail不为空则添加到结果中
    if (question.detail.length > 0) {
      questions.push(question)
    }
  }
  
  return questions
}

// 解析整个CSV字符串，正确处理带引号和换行符的字段
const parseCSVString = (csvStr) => {
  const parts = []
  let inQuotes = false
  let currentPart = ''
  
  for (let i = 0; i < csvStr.length; i++) {
    const char = csvStr[i]
    
    if (char === '"') {
      if (i + 1 < csvStr.length && csvStr[i + 1] === '"') {
        // 处理双引号转义
        currentPart += '"'
        i++
      } else {
        inQuotes = !inQuotes
      }
    } else if (char === ',' && !inQuotes) {
      parts.push(currentPart)
      currentPart = ''
    } else {
      currentPart += char
    }
  }
  
  if (currentPart) {
    parts.push(currentPart)
  }
  
  return parts
}

// 旧的解析CSV行函数，保留用于解析其他类型的CSV数据
const parseCSVLine = (line) => {
  const parts = []
  let inQuotes = false
  let currentPart = ''
  
  for (let i = 0; i < line.length; i++) {
    const char = line[i]
    
    if (char === '"') {
      if (i + 1 < line.length && line[i + 1] === '"') {
        // 处理双引号转义
        currentPart += '"'
        i++
      } else {
        inQuotes = !inQuotes
      }
    } else if (char === ',' && !inQuotes) {
      parts.push(currentPart)
      currentPart = ''
    } else {
      currentPart += char
    }
  }
  
  if (currentPart) {
    parts.push(currentPart)
  }
  
  return parts
}

// 新增：将旧格式的题目（title和options）转换为新格式（detail数组）
const convertToDetailFormat = (question) => {
  // 如果已经是新格式，直接返回
  if (question.detail && Array.isArray(question.detail)) {
    return question;
  }
  
  // 如果是旧格式
  if (question.title || question.options) {
    const convertedQuestion = { ...question };
    // 创建detail数组
    convertedQuestion.detail = [];
    
    // 添加题干
    if (question.title) {
      convertedQuestion.detail.push('题干');
      convertedQuestion.detail.push(question.title);
    }
    
    // 添加选项
    if (Array.isArray(question.options)) {
      for (const option of question.options) {
        convertedQuestion.detail.push('选项');
        convertedQuestion.detail.push(option);
      }
    }
    
    return convertedQuestion;
  }
  
  // 如果既没有detail也没有title/options，返回原始对象，确保有detail数组
  return {
    ...question,
    detail: question.detail || []
  };
};

// 更新试卷的题目列表
const updatePaperQuestions = async (paperId, questionIds) => {
  const db = await initDB()
  const paper = await db.get(papersStoreName, paperId)
    paper.questionIds = questionIds
    await db.put(papersStoreName, paper)
  
}

// 添加防抖变量
const syncDebounceTimer = ref(null);
const lastSyncedData = ref({
  papers: [],
  questions: []
});

// 更新 uploadDataToCloud 函数，只发送变更的数据，并且不包含stars属性
const uploadDataToCloud = async () => {
  const db = await initDB();
  
  // 获取所有本地数据
  // 在新的数据结构中，试卷信息存储在config.papers中
  const papersData = await safeGetConfig('papers', {});
  const localPapers = [];
  
  // 收集所有试卷和题目
  const localQuestions = [];
  
  // 转换试卷映射为数组
  for (const paperId in papersData) {
    const paperName = papersData[paperId];
    const paperStoreName = getPaperStoreName(paperId);
    
    // 验证对应的存储区是否存在
    if (db.objectStoreNames.contains(paperStoreName)) {
      // 获取此试卷的所有题目
      const questions = await db.getAll(paperStoreName);
      
      // 提取题目ID
      const questionIds = questions.map(q => q.id);
      
      // 创建试卷对象
      const paper = {
        id: paperId,
        name: paperName,
        questionIds
      };
      
      localPapers.push(paper);
      
      // 收集题目
      localQuestions.push(...questions);
    }
  }
  
  // 跟踪上次同步的数据对比，找出变化的数据
  const changedPapers = [];
  const changedQuestions = [];
  
  // 查找本地新增或变更的试卷
  const lastSyncedPapersMap = new Map(lastSyncedData.value.papers.map(p => [p.id, JSON.stringify(p)]));
  for (const paper of localPapers) {
    const lastPaperStr = lastSyncedPapersMap.get(paper.id);
    // 如果是新试卷或内容有变更，加入变更列表
    if (!lastPaperStr || lastPaperStr !== JSON.stringify(paper)) {
      changedPapers.push(paper);
    }
  }
  
  // 查找本地新增或变更的题目
  const lastSyncedQuestionsMap = new Map(lastSyncedData.value.questions.map(q => [q.id, JSON.stringify(q)]));
  for (const question of localQuestions) {
    // 创建不包含stars属性的题目副本
    const questionWithoutStars = { ...question };
    if ('stars' in questionWithoutStars) {
      delete questionWithoutStars.stars;
    }
    
    const lastQuestionStr = lastSyncedQuestionsMap.get(question.id);
    
    // 比较不含stars属性的题目
    let lastQuestionWithoutStars = null;
    if (lastQuestionStr) {
      lastQuestionWithoutStars = JSON.parse(lastQuestionStr);
      if ('stars' in lastQuestionWithoutStars) {
        delete lastQuestionWithoutStars.stars;
      }
    }
    
    // 如果是新题目或内容有变更，加入变更列表
    if (!lastQuestionWithoutStars || JSON.stringify(lastQuestionWithoutStars) !== JSON.stringify(questionWithoutStars)) {
      changedQuestions.push(questionWithoutStars);
    }
  }
  
  // 如果没有变更的数据，无需同步
  if (changedPapers.length === 0 && changedQuestions.length === 0) {
    console.log('没有变更的数据，跳过同步');
    return { success: true, message: '没有需要同步的数据' };
  }
  
  console.log(`准备同步变更数据: ${changedPapers.length} 个试卷, ${changedQuestions.length} 个题目`);
  
  // 构建上传数据
  const uploadData = {
    papers: changedPapers,
    questions: changedQuestions
  };
  
  // 构建API URL
  const url = syncConfig.value.backendUrl;
  
  // 确保URL中包含问号
  const apiUrl = url.includes('?') ? `${url}&type=all` : `${url}?type=all`;
  
  // 发送数据到后端
  const response = await fetch(apiUrl, {
    method: 'PUT',
    headers: {
      'Content-Type': 'application/json'
    },
    body: JSON.stringify(uploadData)
  });
  
  if (!response.ok) {
    throw new Error(`上传数据到云端失败: ${response.status}`);
  }
  
  // 更新最后同步的数据
  lastSyncedData.value = {
    papers: JSON.parse(JSON.stringify(localPapers)),
    questions: JSON.parse(JSON.stringify(localQuestions.map(q => {
      const questionWithoutStars = { ...q };
      if ('stars' in questionWithoutStars) {
        delete questionWithoutStars.stars;
      }
      return questionWithoutStars;
    })))
  };
  
  return await response.json();
};

// 跟踪上次同步状态，避免重复同步相同数据
let lastSyncState = {
  lastSyncTime: 0,  // 上次同步时间
  syncInProgress: false,  // 是否有同步正在进行中
  consecutiveSyncs: 0  // 连续同步计数
};

// 原saveQuestion函数已被替换为上面的新版本函数

// 原loadQuestions函数已被替换为上面的新版本函数

// 显示删除确认对话框
const showDeleteDialog = (paper) => {
  paperToDelete.value = paper;
  deleteDialogVisible.value = true;
  console.log(`显示确认删除对话框，准备删除试卷: ${paper.name}(ID: ${paper.id})`);
}

// 旧版本handleDeletePaper函数已被移除，使用上面新实现的版本

// 这些函数已在上面声明过，这里不再重复定义

const switchPaper = async (paper) => {
  // 在新的数据结构中，paper对象已包含所有需要的信息
  currentPaper.value = paper;
  currentQuestions.value = await loadQuestions(paper.id);
    
    // 保存当前试卷ID到localStorage
    localStorage.setItem(CURRENT_PAPER_KEY, paper.id);
    console.log(`已保存当前试卷ID: ${paper.id} 到localStorage`);
    
    // 重置滚动位置
    nextTick(() => {
      contentListRef.value?.scrollTo({
        index: 0,
        behavior: 'instant'
      });
    });
    
    // 自动关闭抽屉
    drawerVisible.value = false;
};

const renamePaper = (paper) => {
  paperToRename.value = paper
  newPaperName.value = paper.name
  renameDialogVisible.value = true
}

const confirmRenamePaper = async () => {
  if (!paperToRename.value || !newPaperName.value.trim()) {
    return
  }
  
  const paperName = newPaperName.value.trim()
  
  // 从config表中获取papers映射并更新
  const papersMap = await safeGetConfig('papers', {})
  papersMap[paperToRename.value.id] = paperName
  await safePutConfig('papers', papersMap)
  
  // 更新状态
  const paperIndex = papers.value.findIndex(p => p.id === paperToRename.value.id)
  if (paperIndex !== -1) {
    papers.value[paperIndex].name = paperName
  }
  
  // 如果重命名的是当前试卷，也更新当前试卷
  if (currentPaper.value && currentPaper.value.id === paperToRename.value.id) {
    currentPaper.value.name = paperName
  }
  
  // 重置状态
  renameDialogVisible.value = false
  paperToRename.value = null
  newPaperName.value = ''
}

const handleAddPaper = () => {
  isAddingPaper.value = true
  newPaperName.value = ''
}

const confirmAddPaper = async () => {
  if (!newPaperName.value.trim()) return
  
  const paper = await addPaper(newPaperName.value.trim())
  currentPaper.value = paper
    currentQuestions.value = []
  
  // 重置表单
    newPaperName.value = ''
  isAddingPaper.value = false
  
  // 不需要在这里调用同步，因为 addPaper 函数中已经调用了
}

const cancelAddPaper = () => {
  isAddingPaper.value = false
  newPaperName.value = ''
}

// 获取AI解析
const handleGetAIAnalysis = async () => {
  // 如果没有当前试卷，或者正在进行中，则不处理
  if (!currentPaper.value || aiAnalysisProgress.value.inProgress) {
    return;
  }
  
  try {
    // 加载当前试卷的所有题目
    const paperStoreName = getPaperStoreName(currentPaper.value.id);
    const db = await initDB();
    
    // 验证存储区是否存在
    if (!db.objectStoreNames.contains(paperStoreName)) {
      console.warn(`试卷存储区 ${paperStoreName} 不存在`);
      return;
    }
    
    // 从试卷存储区加载所有题目
    const questions = await db.getAll(paperStoreName);
    
    // 过滤出没有解析的题目
    const questionsWithoutAnalysis = questions.filter(q => !q.answer || q.answer.trim() === '');
    
    // 更新进度信息
    aiAnalysisProgress.value.total = questions.length;
    aiAnalysisProgress.value.processed = questions.length - questionsWithoutAnalysis.length;
    aiAnalysisProgress.value.inProgress = true;
    
    console.log(`开始处理AI解析: 共${questions.length}道题，已有解析${aiAnalysisProgress.value.processed}道，需处理${questionsWithoutAnalysis.length}道`);
    
    // 变量标记是否有任何题目被更新
    let hasUpdatedAnyQuestion = false;
    
    // 逐个处理题目
    for (const question of questionsWithoutAnalysis) {
      // 确保不重复处理已有解析的题目
      if (question.answer && question.answer.trim() !== '') {
        continue;
      }
      
      try {
        // 生成解析
        await generateAnalysis(question);
        
        // 更新进度
        aiAnalysisProgress.value.processed += 1;
        
        // 标记已更新
        hasUpdatedAnyQuestion = true;
        
        // 每处理一个题目就更新试卷的最后修改时间
        await updatePaperLastModifiedTime(currentPaper.value.id);
      } catch (error) {
        console.error(`处理题目 ${question.id} 时出错:`, error);
        // 继续处理下一个题目
      }
    }
    
    // 完成后刷新题目列表
    currentQuestions.value = await loadQuestions(currentPaper.value.id);
    
    // 如果有任何题目被更新，执行同步
    if (hasUpdatedAnyQuestion && syncConfig.value.syncEnabled) {
      console.log('生成解析后添加到同步计划...');
      try {
        // 执行同步操作
        await manualSync();
      } catch (syncError) {
        console.error('同步时发生错误:', syncError);
      }
    }
    
    console.log('AI解析处理完成');
  } catch (error) {
    console.error('处理AI解析时发生错误:', error);
  } finally {
    // 无论成功或失败，最终都设置进度状态为完成
    aiAnalysisProgress.value.inProgress = false;
  }
}

const handleStickyClick = async (context) => {
  if (context.item.label === '设置') {
    await showConfigDrawer()
    activeTab.value = 'ui'
  } else if (context.item.label === '同步') {
    console.log('通过handleStickyClick调用同步功能');
    await floatingToolbarSync();
  }
}

// 悬浮工具栏专用的同步函数
const floatingToolbarSync = async () => {
  try {
    console.log('从悬浮工具栏执行同步...');
    // 设置一个状态标志，指示我们是从悬浮工具栏同步的
    const isFloatingToolbarSync = true;
    // 预先设置标记以防止抖动
    hasPendingSync.value = true;
    
    // 在try块中包装manualSync调用，这样即使内部抛出错误也能保持hasPendingSync = true
    try {
      // 执行手动同步
      await manualSync();
    } catch (syncError) {
      // 确保状态正确
      syncError.value = true;
      hasPendingSync.value = true;
      throw syncError; // 重新抛出以便被外层catch捕获
    }
  } catch (error) {
    console.error('悬浮工具栏同步失败:', error);
    // 确保错误状态被设置
    syncError.value = true;
    // 确保按钮继续显示
    hasPendingSync.value = true;
    
    // 显示错误通知
    NotifyPlugin.error({
      title: '同步失败',
      content: error.message,
      placement: 'top-left',
      duration: 3000
    });
  }
};

// 处理同步按钮点击
const handleSyncClick = async (event) => {
  // 阻止事件冒泡
  if (event) {
    event.stopPropagation();
  }
  console.log('同步按钮被点击，准备执行专用同步函数');
  await floatingToolbarSync();
}

// 处理设置按钮点击
const handleSettingsClick = (event) => {
  // 确保阻止事件冒泡
  if (event) {
    event.stopPropagation();
    event.preventDefault();
  }
  console.log('设置按钮被点击');
  // 在这里添加打开设置面板的逻辑
}

// 为同步按钮添加触摸事件
const setupSyncButtonTouchEvent = () => {
  nextTick(() => {
    const syncButton = document.querySelector('.sync-button');
    if (syncButton) {
      console.log('为同步按钮添加触摸事件');
      
      // 移除可能已存在的事件监听器
      syncButton.removeEventListener('touchend', handleSyncTouchEnd);
      
      // 添加新的触摸事件监听器，使用捕获阶段
      syncButton.addEventListener('touchend', handleSyncTouchEnd, { capture: true });
    }
  });
}

// 处理同步按钮的触摸结束事件
const handleSyncTouchEnd = (event) => {
  // 获取触摸点和同步按钮的位置信息
  const touch = event.changedTouches[0];
  const syncButton = document.querySelector('.sync-button');
  
  if (syncButton) {
    const buttonRect = syncButton.getBoundingClientRect();
    
    // 检查触摸点是否在同步按钮区域内
    const isInSyncButton = 
      touch.clientX >= buttonRect.left && 
      touch.clientX <= buttonRect.right && 
      touch.clientY >= buttonRect.top && 
      touch.clientY <= buttonRect.bottom;
    
    // 仅当触摸点在同步按钮区域内时才触发同步
    if (isInSyncButton) {
      console.log('同步按钮触摸结束');
      event.preventDefault();
      event.stopPropagation();
      handleSyncClick();
    }
  }
}

// 切换暗黑模式
const toggleDarkMode = () => {
  isDarkMode.value = !isDarkMode.value
  if (isDarkMode.value) {
    // 设置暗色模式
    document.documentElement.setAttribute('theme-mode', 'dark')
    // 更新状态栏颜色为深色
    document.querySelector('meta[name="theme-color"][media="(prefers-color-scheme: dark)"]').setAttribute('content', '#242424')
    document.querySelector('meta[name="theme-color"][media="(prefers-color-scheme: light)"]').setAttribute('content', '#242424')
  } else {
    // 重置为浅色模式
    document.documentElement.removeAttribute('theme-mode')
    // 更新状态栏颜色为浅色
    document.querySelector('meta[name="theme-color"][media="(prefers-color-scheme: dark)"]').setAttribute('content', '#ffffff')
    document.querySelector('meta[name="theme-color"][media="(prefers-color-scheme: light)"]').setAttribute('content', '#ffffff')
  }
  
  // 保存当前主题模式到localStorage
  localStorage.setItem('theme-mode', isDarkMode.value ? 'dark' : 'light')
  
  // 检测是否是Safari浏览器
  const isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent)
  
  // 仅在Safari浏览器中重新加载页面
  if (isSafari) {
    window.location.reload()
  }
}

const startDrag = (e) => {
  e.preventDefault()
  e.stopPropagation()
  
  isDragging = true
  startX = e.clientX
  startY = e.clientY
  
  const tool = stickyToolRef.value.$el
  console.log('工具栏元素:', tool)
  const rect = tool.getBoundingClientRect()
  console.log('工具栏位置:', rect)
  currentX = rect.left
  currentY = rect.top

  const handleMouseMove = (e) => {
    if (!isDragging) return
    
    const dx = e.clientX - startX
    const dy = e.clientY - startY
    
    const tool = stickyToolRef.value.$el
    const newLeft = currentX + dx
    const newTop = currentY + dy
    
    tool.style.position = 'fixed'
    tool.style.left = `${newLeft}px`
    tool.style.top = `${newTop}px`
    tool.style.right = 'auto'
    tool.style.bottom = 'auto'
  }

  const handleMouseUp = () => {
    isDragging = false
    document.removeEventListener('mousemove', handleMouseMove)
    document.removeEventListener('mouseup', handleMouseUp)
    saveToolbarPosition()
    
    // 在拖动结束后重置工具栏大小，确保所有按钮都正确显示
    setTimeout(() => {
      resetStickyToolSize()
    }, 50)
  }

  document.addEventListener('mousemove', handleMouseMove)
  document.addEventListener('mouseup', handleMouseUp)
}

const startTouchDrag = (e) => {
  console.log('开始触摸拖动', e)
  e.preventDefault()
  e.stopPropagation()
  
  isDragging = true
  startX = e.touches[0].clientX
  startY = e.touches[0].clientY
  
  const tool = stickyToolRef.value.$el
  console.log('工具栏元素:', tool)
  const rect = tool.getBoundingClientRect()
  console.log('工具栏位置:', rect)
  currentX = rect.left
  currentY = rect.top

  const handleTouchMove = (e) => {
    if (!isDragging) return
    
    console.log('触摸拖动中', e.touches[0].clientX, e.touches[0].clientY)
    const dx = e.touches[0].clientX - startX
    const dy = e.touches[0].clientY - startY
    
    const tool = stickyToolRef.value.$el
    const newLeft = currentX + dx
    const newTop = currentY + dy
    console.log('新位置:', newLeft, newTop)
    
    tool.style.position = 'fixed'
    tool.style.left = `${newLeft}px`
    tool.style.top = `${newTop}px`
    tool.style.right = 'auto'
    tool.style.bottom = 'auto'
  }

  const handleTouchEnd = () => {
    console.log('停止触摸拖动')
    isDragging = false
    document.removeEventListener('touchmove', handleTouchMove, { passive: false })
    document.removeEventListener('touchend', handleTouchEnd)
    saveToolbarPosition()
    
    // 在拖动结束后重置工具栏大小，确保所有按钮都正确显示
    setTimeout(() => {
      resetStickyToolSize()
    }, 50)
  }

  document.addEventListener('touchmove', handleTouchMove, { passive: false })
  document.addEventListener('touchend', handleTouchEnd)
}

// 加载服务配置
const loadServiceConfig = async () => {
  try {
    // 先从 localStorage 加载（兼容旧版本）
    const savedConfig = localStorage.getItem(STORAGE_KEY)
    if (savedConfig) {
      serviceConfig.value = JSON.parse(savedConfig)
    }
    
    // 从 IndexedDB 加载最新配置，使用安全函数
    const configData = await safeGetConfig('service_config')
    if (configData) {
      serviceConfig.value = configData
    }
  } catch (error) {
    console.error('加载服务配置失败:', error)
  }
}

// 保存服务配置
const saveServiceConfig = async () => {
  const cleanConfig = {
    token: serviceConfig.value.token || '',
    model: serviceConfig.value.model || '',
    prompt: serviceConfig.value.prompt || defaultConfig.prompt
  }
  
  // 保存到 localStorage（兼容旧版本）
  localStorage.setItem(STORAGE_KEY, JSON.stringify(cleanConfig));
  
  // 保存到 IndexedDB，使用安全函数
  await safePutConfig('service_config', cleanConfig);
  
  // 如果启用了同步，则同步到云端
  if (syncConfig.value.syncEnabled && syncConfig.value.enabledServices?.includes('默认后端服务')) {
    await syncConfigWithBackend(cleanConfig);
  }
  
  // 添加：数据更新后同步到云端 - 不需要在这里调用，因为配置数据与题目/试卷数据分开同步
}

// 服务配置变更处理
const handleServiceConfigChange = async () => {
  await saveServiceConfig()
}

// 保存工具栏位置
const saveToolbarPosition = () => {
  const tool = stickyToolRef.value.$el
  const rect = tool.getBoundingClientRect()
  localStorage.setItem(TOOLBAR_POSITION_KEY, JSON.stringify({
    left: rect.left,
    top: rect.top
  }))
}

// 恢复工具栏位置
const restoreToolbarPosition = () => {
  const savedPosition = localStorage.getItem(TOOLBAR_POSITION_KEY)
  if (savedPosition) {
    try {
      const { left, top } = JSON.parse(savedPosition)
      const tool = stickyToolRef.value.$el
      
      // 获取窗口尺寸和工具栏尺寸
      const windowWidth = window.innerWidth
      const windowHeight = window.innerHeight
      const toolWidth = tool.offsetWidth
      const toolHeight = tool.offsetHeight
      
      // 检查保存的位置是否会导致工具栏超出可视区域
      const isOutOfBounds = 
        left < 0 || 
        top < 0 || 
        left + toolWidth > windowWidth || 
        top + toolHeight > windowHeight
      
      if (isOutOfBounds) {
        // 如果超出可视区域，重置到默认位置（右下角）
        tool.style.position = 'fixed'
        tool.style.right = '24px'
        tool.style.bottom = '24px'
        tool.style.left = 'auto'
        tool.style.top = 'auto'
        console.log('工具栏位置已重置，因为保存的位置超出了可视区域')
      } else {
        // 使用保存的位置
        tool.style.position = 'fixed'
        tool.style.left = `${left}px`
        tool.style.top = `${top}px`
        tool.style.right = 'auto'
        tool.style.bottom = 'auto'
      }
    } catch (error) {
      console.error('恢复工具栏位置失败:', error)
      // 如果解析出错，重置到默认位置
      const tool = stickyToolRef.value.$el
      tool.style.position = 'fixed'
      tool.style.right = '24px'
      tool.style.bottom = '24px'
      tool.style.left = 'auto'
      tool.style.top = 'auto'
    }
  }
}

// 处理题目点击事件
const handleQuestionClick = async (question) => {
  // 如果解析框未显示，则显示并获取解析
  if (!question.showAnalysis) {
    question.showAnalysis = true;
    
    // 在标记解析框显示后立即设置MutationObserver来监听DOM变化
    setAnalysisObserver();
    
    // 添加调试日志
    console.log('准备获取题目解析，题目ID:', question.id);
    console.log('题目内容:', JSON.stringify(question));
    
    // 只使用本地数据
    console.log('使用本地数据或AI生成解析');
    await fallbackQuestionAnalysis(question);
  }
}

// 强制刷新题目，确保解析内容显示
const forceRefreshQuestion = async (question) => {
  // 首先找到题目在当前列表中的索引
  const index = currentQuestions.value.findIndex(q => q.id === question.id);
  if (index !== -1) {
    console.log('正在强制刷新题目显示...');
    
    // 创建一个新的对象引用，以触发Vue的响应式更新
    const refreshedQuestion = { 
      ...currentQuestions.value[index],
      // 确保解析属性被正确复制
      answer: question.answer,
      simpleAnswer: question.simpleAnswer,
      showAnalysis: true
    };
    
    // 使用splice替换当前题目，触发Vue的响应式更新
    currentQuestions.value.splice(index, 1, refreshedQuestion);
    
    // 等待下一个渲染周期
    await nextTick();
    
    // 找到解析元素并直接更新内容
    const contentArea = document.querySelector('.content-area');
    if (contentArea) {
      const questionElements = contentArea.querySelectorAll('.question-content');
      if (questionElements && questionElements.length > index) {
        const currentQuestionElement = questionElements[index];
        const simpleAnalysisElement = currentQuestionElement.querySelector('.simple-analysis');
        const detailedAnalysisElement = currentQuestionElement.querySelector('.detailed-analysis');
        
        if (simpleAnalysisElement) {
          simpleAnalysisElement.textContent = formatText(question.simpleAnswer) || '正在生成解析...';
        }
        
        if (detailedAnalysisElement) {
          detailedAnalysisElement.textContent = formatText(question.answer) || '';
        }
        
        console.log('已直接更新DOM元素内容');
      }
    }
  }
};

// 设置解析框观察器，监听解析框的出现并立即应用字体大小
const setAnalysisObserver = () => {
  // 如果已经有观察器，先断开连接
  if (analysisMutationObserver) {
    analysisMutationObserver.disconnect();
  }

  // 创建一个新的MutationObserver实例
  analysisMutationObserver = new MutationObserver((mutations) => {
    for (const mutation of mutations) {
      if (mutation.type === 'childList') {
        // 寻找是否有新增的解析框
        const addedAnalysis = Array.from(mutation.addedNodes).find(node => 
          node.nodeType === Node.ELEMENT_NODE && 
          (node.classList?.contains('analysis') || node.querySelector?.('.analysis'))
        );
        
        if (addedAnalysis) {
          console.log('解析框已添加到DOM，立即应用字体大小和透明度');
          // 立即应用字体大小
          applyFontSize(uiConfig.value.fontSize);
          
          // 如果是暗黑模式，还需要应用文字透明度
          if (uiConfig.value.darkMode) {
            // 计算不透明度值（1 - 透明度/100）
            const opacityValue = 1 - (uiConfig.value.textOpacity / 100);
            
            // 找到所有新添加的解析内容
            const analysisElements = addedAnalysis.classList?.contains('analysis') 
              ? [addedAnalysis] 
              : Array.from(addedAnalysis.querySelectorAll('.analysis'));
            
            for (const el of analysisElements) {
              // 获取简洁解析和详细解析元素
              const simpleAnalysis = el.querySelector('.simple-analysis');
              const detailedAnalysis = el.querySelector('.detailed-analysis');
              
              if (simpleAnalysis) {
                simpleAnalysis.style.fontSize = `${18 + uiConfig.value.fontSize - 2}px`;
                // 应用透明度
                simpleAnalysis.style.opacity = opacityValue.toString();
              }
              
              if (detailedAnalysis) {
                detailedAnalysis.style.fontSize = `${18 + uiConfig.value.fontSize - 2}px`;
                // 应用透明度
                detailedAnalysis.style.opacity = opacityValue.toString();
              }
            }
          } else {
            // 找到所有新添加的解析内容
            const analysisElements = addedAnalysis.classList?.contains('analysis') 
              ? [addedAnalysis] 
              : Array.from(addedAnalysis.querySelectorAll('.analysis'));
            
            for (const el of analysisElements) {
              // 获取简洁解析和详细解析元素
              const simpleAnalysis = el.querySelector('.simple-analysis');
              const detailedAnalysis = el.querySelector('.detailed-analysis');
              
              if (simpleAnalysis) {
                simpleAnalysis.style.fontSize = `${18 + uiConfig.value.fontSize - 2}px`;
              }
              
              if (detailedAnalysis) {
                detailedAnalysis.style.fontSize = `${18 + uiConfig.value.fontSize - 2}px`;
              }
            }
          }
          
          // 完成后断开观察器
          analysisMutationObserver.disconnect();
          analysisMutationObserver = null;
          break;
        }
      }
    }
  });
  
  // 开始观察内容区域的DOM变化
  const contentArea = document.querySelector('.content-area');
  if (contentArea) {
    analysisMutationObserver.observe(contentArea, {
      childList: true,
      subtree: true
    });
    console.log('已设置解析框DOM观察器');
  }
};

// 降级处理 - 当云同步出错或未启用时使用
const fallbackQuestionAnalysis = async (question) => {
  try {
    // 获取IndexedDB的题目数据
    const paperStoreName = getPaperStoreName(currentPaper.value.id)
    const db = await initDB()
    const localQuestion = db.objectStoreNames.contains(paperStoreName) ? 
      await db.get(paperStoreName, question.id) : null
    
    if (!localQuestion || !localQuestion.answer || localQuestion.answer === "") {
      // 本地没有解析，通过AI服务获取
      console.log('本地没有解析或解析为空，通过AI服务获取')
      await generateAnalysis(question)
    } else {
      // 使用本地解析
      question.answer = localQuestion.answer
      question.simpleAnswer = localQuestion.simpleAnswer || ''
      question.correctChoice = localQuestion.correctChoice || ''
      
      // 在加载本地解析后应用字体大小
      await nextTick()
      setTimeout(() => {
        applyFontSize(uiConfig.value.fontSize)
        // 如果是暗黑模式，还需要应用文字透明度
        if (uiConfig.value.darkMode) {
          applyTextOpacity(uiConfig.value.textOpacity)
        }
      }, 100)
    }
  } catch (error) {
    console.error('降级处理题目解析时出错:', error)
    // 如果还是出错，尝试直接生成解析
    await generateAnalysis(question)
  }
}

// 重试生成解析
const retryGenerateAnalysis = async (question) => {
  question.answer = '正在生成解析...';
  
  // 在重新生成解析前设置观察器
  setAnalysisObserver();
  
  await generateAnalysis(question);
}

// 生成AI解析
const generateAnalysis = async (question) => {
  try {
    console.log('开始生成解析，题目ID:', question.id);
    
    // 定义本地的 papersToDownload 变量
    const papersToDownload = [];
    
    // 确保题目是detail格式
    const convertedQuestion = convertToDetailFormat(question);
    
    // 准备详细内容
    let detailText = '';
    
    // 从detail列表中提取内容
    if (convertedQuestion.detail && Array.isArray(convertedQuestion.detail)) {
      // 将detail列表转换为易读的文本
      for (let i = 0; i < convertedQuestion.detail.length; i += 2) {
        if (i + 1 >= convertedQuestion.detail.length) break;
        
        const type = convertedQuestion.detail[i];
        const content = convertedQuestion.detail[i + 1];
        
        // 跳过类型，只使用内容
        detailText += content + '\n';
      }
    }
    
    console.log('发送给AI的题目内容:', detailText);
    
    // 准备提示语
    const prompt = serviceConfig.value.prompt + detailText;
    
    const options = {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${serviceConfig.value.token}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        model: serviceConfig.value.model,
        messages: [
          {
            role: 'system',
            content: 'You are a helpful assistant designed to output JSON. Please respond in the format: {"简洁解析":"...", "详细解析":"..."}. All values in JSON must be strings expressed in natural language. Use Chinese double quotation marks by default instead of Chinese single quotation marks or English quotation marks.'
          },
          {
            role: 'user',
            content: prompt
          }
        ],
        stream: false,
        max_tokens: 4096,
        temperature: 0.1,
        top_p: 0.7,
        top_k: 50,
        frequency_penalty: 0.5,
        n: 1,
        response_format: { type: 'json_object' }
      })
    }

    console.log('发送AI请求...');
    const response = await fetch('https://api.siliconflow.cn/v1/chat/completions', options)
    
    if (!response.ok) {
      console.error('AI请求失败，状态码:', response.status);
      const responseText = await response.text();
      console.error('错误响应:', responseText);
      throw new Error(`HTTP error! status: ${response.status}`)
    }
    
    const data = await response.json()
    console.log('AI响应数据:', JSON.stringify(data));
    
    if (data.choices && data.choices[0] && data.choices[0].message) {
      const analysis = data.choices[0].message.content
      console.log('AI返回的原始解析:', analysis);
      
      if (analysis) {
        const analysisData = JSON.parse(analysis)
        console.log('解析结果:', JSON.stringify(analysisData));
        
        // 处理可能的嵌套 JSON 结构
        let simpleAnswer = ''
        let detailedAnswer = ''
        
        if (typeof analysisData.简洁解析 === 'string') {
          // 标准格式
          simpleAnswer = analysisData.简洁解析
          console.log('简洁解析(标准格式):', simpleAnswer);
        } else if (typeof analysisData.简洁解析 === 'object') {
          // 嵌套格式，将对象转换为文本
          // 简洁解析使用"、"连接各个答案，形成一句话
          simpleAnswer = Object.entries(analysisData.简洁解析)
            .map(([key, value]) => {
              // 判断值是否已经以标点符号结尾
              const valueText = value.toString().trim();
              const needsPunctuation = !/[。？！，、.?!,]$/.test(valueText);
              return `${key}${needsPunctuation ? '：' : ' '}${valueText}${needsPunctuation ? '。' : ''}`;
            })
            .join(' ');
          console.log('简洁解析(嵌套格式):', simpleAnswer);
        }
        
        if (typeof analysisData.详细解析 === 'string') {
          // 标准格式
          detailedAnswer = analysisData.详细解析
          console.log('详细解析(标准格式):', detailedAnswer);
        } else if (typeof analysisData.详细解析 === 'object') {
          // 嵌套格式，将对象转换为文本
          // 详细解析每个问题单独成段落
          detailedAnswer = Object.entries(analysisData.详细解析)
            .map(([key, value]) => `【${key}】\n${value}`)
            .join('\n\n');
          console.log('详细解析(嵌套格式):', detailedAnswer);
        }
        
        // 更新题目解析
        question.answer = detailedAnswer
        question.simpleAnswer = simpleAnswer
        
        console.log('更新后的题目解析:', question.answer);
        console.log('更新后的简洁解析:', question.simpleAnswer);
        
        // 更新IndexedDB中的解析
        // 获取题目所在的试卷存储区
        const paperStoreName = getPaperStoreName(currentPaper.value.id)
        const db = await initDB()
        
        // 验证存储区是否存在
        if (db.objectStoreNames.contains(paperStoreName)) {
          const existingQuestion = await db.get(paperStoreName, question.id)
        if (existingQuestion) {
          existingQuestion.answer = detailedAnswer
          existingQuestion.simpleAnswer = simpleAnswer
          // 不再更新星级评分
            await db.put(paperStoreName, existingQuestion)
          console.log('解析已保存到IndexedDB');
          
          // 确保在保存解析后立即更新试卷的修改时间
          try {
            const updateResult = await updatePaperLastModifiedTime(currentPaper.value.id);
            console.log(`保存解析后更新试卷时间结果: ${updateResult ? '成功' : '失败'}`);
          } catch (updateTimeError) {
            console.error('保存解析后更新试卷时间时出错:', updateTimeError);
          }
          }
        } else {
          console.error(`试卷存储区 ${paperStoreName} 不存在，无法保存解析`);
        }
        
        // 刷新UI
        if (currentPaper.value && papersToDownload.includes(currentPaper.value.id)) {
          console.log(`当前试卷(${currentPaper.value.id})在下载列表中，需要刷新DOM`);
          currentQuestions.value = await loadQuestions(currentPaper.value.id);
        } else if (papersToDownload.length > 0) {
          console.log('下载的试卷与当前打开的试卷不同，无需刷新DOM');
        }
        
        // 立即应用字体大小设置，不使用setTimeout延迟
        await nextTick();
        applyFontSize(uiConfig.value.fontSize);
        
        // 如果是暗黑模式，还需要应用文字透明度
        if (uiConfig.value.darkMode) {
          applyTextOpacity(uiConfig.value.textOpacity);
        }
        
        // 更新试卷的最后修改时间
        try {
          const updateResult = await updatePaperLastModifiedTime(currentPaper.value.id);
          console.log(`更新试卷时间结果: ${updateResult ? '成功' : '失败'}`);
        } catch (updateTimeError) {
          console.error('更新试卷时间时出错:', updateTimeError);
        }
      } else {
        console.error('无解析内容');
        throw new Error('No analysis content')
      }
    } else {
      console.error('AI响应格式无效');
      throw new Error('Invalid response format')
    }
  } catch (error) {
    console.error('生成解析失败:', error);
    console.log('错误详情:', error.message, error.stack);
    question.answer = '生成解析失败，请稍后重试'
    
    // 即使出错也应用字体大小
    await nextTick();
    applyFontSize(uiConfig.value.fontSize);
    
    // 如果是暗黑模式，还需要应用文字透明度
    if (uiConfig.value.darkMode) {
      applyTextOpacity(uiConfig.value.textOpacity);
    }
    
    // 不在AI生成解析失败时更新试卷最后修改时间，避免触发不必要的同步
  }
}

// 处理列表滚动
let scrollThrottleTimer = null;
const THROTTLE_DELAY = 300; // 节流延迟，300毫秒

const handleListScroll = (e) => {
  // 用户主动滚动时，允许下次加载页面恢复滚动位置
  if (!scrollThrottleTimer && scrollRestoreAttempted) {
    // 标记为已经处理过滚动，延迟一段时间后再允许触发滚动恢复
    // 这样用户滚动后，如果刷新页面仍然可以恢复到上次位置
    setTimeout(() => {
      scrollRestoreAttempted = false;
    }, 1000);
  }
  
  // 使用节流函数减少触发频率
  if (scrollThrottleTimer) return;
  
  scrollThrottleTimer = setTimeout(() => {
    // 获取当前滚动位置
    const listElement = document.querySelector('.content-list-container');
    if (!listElement) return;
    
    // 获取所有题目元素
    const questionElements = listElement.querySelectorAll('.question-content');
    if (!questionElements.length) return;
    
    // 获取列表容器的视口信息
    const listRect = listElement.getBoundingClientRect();
    const viewportTop = listRect.top;
    const viewportHeight = listRect.height;
    const viewportCenter = viewportTop + (viewportHeight / 2);
    
    let bestVisibleIndex = 0;
    let bestVisibleArea = 0;
    
    // 计算当前视口中最大可见面积的题目
    for (let i = 0; i < questionElements.length; i++) {
      const element = questionElements[i];
      const elementRect = element.getBoundingClientRect();
      
      // 计算元素在视口中的可见部分
      const visibleTop = Math.max(elementRect.top, viewportTop);
      const visibleBottom = Math.min(elementRect.bottom, viewportTop + viewportHeight);
      
      if (visibleBottom > visibleTop) {
        // 元素有部分在视口内
        const visibleArea = visibleBottom - visibleTop;
        
        // 如果这个元素可见面积更大，或者它位于视口中心附近
        if (visibleArea > bestVisibleArea || 
            (Math.abs(elementRect.top + elementRect.height/2 - viewportCenter) < 100 && visibleArea > 0)) {
          bestVisibleArea = visibleArea;
          bestVisibleIndex = i;
        }
      }
    }
    
    // 只有当找到可见元素且有当前试卷时才保存
    if (bestVisibleArea > 0 && currentPaper.value?.id) {
      // 保存当前可见度最高的题目索引和当前试卷
      const scrollData = {
        paperId: currentPaper.value.id,
        questionIndex: bestVisibleIndex,
        timestamp: Date.now() // 添加时间戳便于调试
      };
      localStorage.setItem(SCROLL_POSITION_KEY, JSON.stringify(scrollData));
      
      console.log(`已保存滚动位置: 试卷ID=${currentPaper.value?.id}, 题目索引=${bestVisibleIndex}`);
    }
    
    scrollThrottleTimer = null;
  }, THROTTLE_DELAY);
}

// 添加一个标志变量，用于防止滚动函数被重复调用
let scrollRestoreAttempted = false;

// 恢复滚动位置
const restoreScrollPosition = () => {
  // 如果已经尝试过恢复滚动，则不再重复执行
  if (scrollRestoreAttempted) {
    console.log('已经尝试过恢复滚动，跳过重复调用');
    return;
  }
  
  // 设置标志，表示已尝试恢复滚动
  scrollRestoreAttempted = true;
  
  const savedPosition = localStorage.getItem(SCROLL_POSITION_KEY);
  if (!savedPosition) return;
  
  try {
    const { paperId, questionIndex, timestamp } = JSON.parse(savedPosition);
    console.log('尝试恢复滚动位置:', { paperId, questionIndex, savedTime: new Date(timestamp || 0).toLocaleString() });
    
    // 如果当前有试卷打开，且题号有效，则恢复滚动位置（放宽条件，不一定要是同一个试卷）
    if (currentPaper.value && currentQuestions.value && 
        questionIndex >= 0 && 
        questionIndex < currentQuestions.value.length) {
      
      console.log(`将尝试恢复滚动到题目索引: ${questionIndex}`);
      
      // 更可靠的方法来等待DOM完全加载并布局完成
      let retryCount = 0;
      const maxRetries = 10; // 增加最大重试次数
      const checkAndScroll = () => {
        const listElement = document.querySelector('.content-list-container');
        const questionElements = listElement?.querySelectorAll('.question-content');
        
        if (!listElement || !questionElements || questionElements.length <= questionIndex) {
          if (retryCount < maxRetries) {
            console.log(`等待DOM加载... (${retryCount + 1}/${maxRetries})`);
            retryCount++;
            setTimeout(checkAndScroll, 300); // 增加等待时间
            return;
          } else {
            console.log('等待DOM加载超时，无法恢复滚动位置');
            return;
          }
        }
        
        try {
          // 用计算滚动位置的方法
          const calculateScrollPosition = () => {
            // 检查元素是否完全渲染
            const targetElement = questionElements[questionIndex];
            
            // 使用更可靠的方法计算滚动位置
            // 1. 先计算目标元素到列表顶部的距离
            let targetPosition = 0;
            // 累加所有前面题目的高度
            for (let i = 0; i < questionIndex; i++) {
              if (questionElements[i]) {
                targetPosition += questionElements[i].offsetHeight;
              }
            }
            
            // 验证计算的位置是否合理
            console.log(`计算的目标位置: ${targetPosition}px (目标元素高度: ${targetElement.offsetHeight}px)`);
            
            // 确保位置在合理范围内
            if (targetPosition <= 0 && questionIndex > 0) {
              console.log('计算的位置不合理，使用备用方法');
              // 备用方法：直接设置索引
              return questionIndex * 200; // 估计每个题目约200px高
            }
            
            return targetPosition;
          };
          
          // 获取滚动位置
          const scrollPos = calculateScrollPosition();
          
          // 直接滚动到位置，使用一次性滚动，不再分两步
          listElement.scrollTo({
            top: scrollPos,
            behavior: 'auto'
          });
          
          console.log(`已恢复滚动到位置: ${scrollPos}px`);
        } catch (error) {
          console.error('滚动计算出错:', error);
        }
      };
      
      // 给页面充分的渲染时间，然后开始检查
      setTimeout(checkAndScroll, 1200); // 增加初始等待时间，确保DOM完全渲染
    } else {
      console.log('不满足滚动恢复条件:', {
        savedPaperId: paperId,
        currentPaperId: currentPaper.value?.id,
        questionIndex,
        questionsLength: currentQuestions.value.length
      });
    }
  } catch (error) {
    console.error('恢复滚动位置时出错:', error);
  }
}

// 检查同步状态
window.checkSyncStatus = () => {
  console.log(`当前同步状态: hasPendingSync=${hasPendingSync.value}, syncLoading=${syncLoading.value}`);
  return {hasPendingSync: hasPendingSync.value, syncLoading: syncLoading.value};
};

// 修改测试方法
window.testScrollTo = () => {
  const savedPosition = localStorage.getItem(SCROLL_POSITION_KEY);
  console.log('保存的位置:', savedPosition);
  
  if (!savedPosition) {
    console.log('没有找到保存的位置');
    return;
  }
  
  try {
    const { paperId, questionIndex } = JSON.parse(savedPosition);
    console.log('解析的位置信息:', { paperId, questionIndex });
    
    // 获取列表容器和题目元素
    const listElement = document.querySelector('.content-list-container');
    const questionElements = listElement?.querySelectorAll('.question-content');
    
    if (listElement && questionElements && questionElements[questionIndex]) {
      // 获取目标元素
      const targetElement = questionElements[questionIndex];
      
      // 计算目标滚动位置 (元素的顶部位置)
      const targetPosition = targetElement.offsetTop;
      
      // 使用原生滚动，带有平滑效果
      listElement.scrollTo({
        top: targetPosition,
        behavior: 'smooth'
      });
      
      console.log(`已滚动到题目索引 ${questionIndex}, 位置: ${targetPosition}px`);
    } else {
      console.log('没有找到列表容器或题目元素');
    }
  } catch (error) {
    console.error('测试滚动时出错:', error);
  }
}

// 加载星级评分数据
const loadStarsData = async () => {
  try {
    const db = await initDB();
    const starsData = await db.get(configStoreName, 'stars');
    // 使用JSON序列化和反序列化确保返回的是可克隆对象
    const data = starsData?.data || {};
    return JSON.parse(JSON.stringify(data));
  } catch (error) {
    console.error('加载星级评分数据失败:', error);
    return {};
  }
}

// 保存星级评分数据
const saveStarsData = async (starsData) => {
  try {
    const db = await initDB();
    // 使用JSON序列化和反序列化确保保存的是可克隆对象
    const cleanData = JSON.parse(JSON.stringify(starsData));
    
    // 先保存到本地数据库
    await db.put(configStoreName, {
      id: 'stars',
      data: cleanData
    });
    
    console.log('星级评分数据已保存到本地数据库');
    return true; // 返回成功标志
  } catch (error) {
    console.error('保存星级评分数据失败:', error);
    return false;
  }
}

// 与云端同步星级评分数据
const syncStarsWithBackend = async (starsData) => {
  // 检查后端URL是否配置
  if (!syncConfig.value.backendUrl) {
    console.log('后端URL未配置，跳过星级评分同步');
    // 为了向后兼容，我们返回一个成功状态而不是抛出错误
    return { success: true, message: '后端已弃用，数据仅保存在本地' };
  }
  
  try {
    console.log('开始同步星级评分数据到云端:', JSON.stringify(starsData));
    
    // 构建API URL
    const url = syncConfig.value.backendUrl;
    
    // 确保URL中包含问号和token
    let apiUrl = url.includes('?') ? `${url}&type=stars` : `${url}?type=stars`;
    
    // 发送数据到后端
    const response = await fetch(apiUrl, {
      method: 'PUT',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify(starsData)
    });
    
    if (!response.ok) {
      const errorText = await response.text();
      throw new Error(`同步星级评分到云端失败: ${response.status} - ${errorText}`);
    }
    
    const result = await response.json();
    console.log('星级评分同步到云端成功:', result);
    return result;
  } catch (error) {
    console.error('同步星级评分到云端失败:', error);
    throw error; // 抛出错误以便调用者知道同步失败
  }
};

// 从云端获取星级评分数据
const fetchStarsFromBackend = async () => {
  try {
    if (!syncConfig.value.backendUrl) {
      return null;
    }
    
    // 构建API URL
    const url = syncConfig.value.backendUrl;
    
    // 确保URL中包含问号
    const apiUrl = url.includes('?') ? `${url}&type=stars` : `${url}?type=stars`;
    
    const response = await fetch(apiUrl, {
      method: 'GET',
      headers: {
        'Content-Type': 'application/json'
      }
    });
    
    if (!response.ok) {
      throw new Error(`获取云端星级评分失败: ${response.status}`);
    }
    
    const starsData = await response.json();
    console.log('从云端获取星级评分成功');
    
    // 返回原始数据，不在这里存储到 IndexedDB
    return starsData;
  } catch (error) {
    console.error('从云端获取星级评分失败:', error);
    return null;
  }
}

  // 修改评分变化处理函数，接收实际的评分值
const handleStarsChange = async (question, newValue) => {
  console.log('评分变化:', question.id, '旧值:', question.stars, '新值:', newValue);
  
  try {
    // 更新当前问题的stars值
    question.stars = newValue;
    
    // 将星级直接保存到试卷存储区的题目中
    const db = await initDB();
    const paperStoreName = getPaperStoreName(currentPaper.value.id);
    
    // 获取当前题目完整数据
    const fullQuestion = await db.get(paperStoreName, question.id);
    if (fullQuestion) {
      // 更新星级
      fullQuestion.stars = newValue;
      
      // 保存回数据库
      await db.put(paperStoreName, fullQuestion);
      console.log('已将星级值保存到题目数据中:', newValue);
      
      // 更新试卷的最后修改时间
      await updatePaperLastModifiedTime(currentPaper.value.id);
      
      // 检测到星级数据变更，安排同步计划
      scheduleDelayedSync();
    } else {
      console.error('未找到要更新的题目:', question.id);
    }
    
    // 更新已经完成，不需要调用updateCurrentQuestionsStars
  } catch (error) {
    console.error('保存评分失败:', error)
  }
}

// 更新当前题目的星级评分
const updateCurrentQuestionsStars = async () => {
  try {
    // 只有当前有试卷时才能进行更新
    if (!currentPaper.value || !currentPaper.value.id) return;
    
    // 获取试卷存储区名称
    const paperStoreName = getPaperStoreName(currentPaper.value.id);
    
    // 连接数据库
    const db = await initDB();
    
    // 检查存储区是否存在
    if (!db.objectStoreNames.contains(paperStoreName)) {
      console.warn(`试卷存储区 ${paperStoreName} 不存在，无法更新星级评分`);
      return;
    }
    
    // 获取所有题目
    const questions = await db.getAll(paperStoreName);
    
    // 更新当前显示的题目的星级，但保持引用不变，只更新内部属性
    if (currentQuestions.value) {
      currentQuestions.value.forEach(displayQuestion => {
        // 在数据库中查找对应题目
        const dbQuestion = questions.find(q => q.id === displayQuestion.id);
        // 如果找到了，更新星级
        if (dbQuestion) {
          displayQuestion.stars = dbQuestion.stars || 0;
        }
      });
    }
    
    console.log('所有题目的星级评分已更新');
  } catch (error) {
    console.error('更新题目星级失败:', error);
  }
}

// 加载云同步配置
const loadSyncConfig = () => {
  const savedConfig = localStorage.getItem(SYNC_CONFIG_KEY)
  if (savedConfig) {
    const parsedConfig = JSON.parse(savedConfig);
    // 合并配置，确保enabledServices属性存在
    syncConfig.value = {
      ...syncConfig.value,
      ...parsedConfig,
      enabledServices: parsedConfig.enabledServices || ['默认后端服务']
    };
  }
  
  // 从localStorage直接读取syncEnabled状态
  const syncEnabled = localStorage.getItem('syncEnabled')
  if (syncEnabled !== null) {
    syncConfig.value.syncEnabled = syncEnabled === 'true'
  }
  
  // 始终确保enabledServices属性存在
  if (!syncConfig.value.enabledServices) {
    syncConfig.value.enabledServices = ['默认后端服务'];
  }
}

// 清理S3 Endpoint URL，确保不包含存储桶名称
const cleanEndpointUrl = (url) => {
  if (!url) return '';
  
  try {
    // 移除URL末尾的斜杠
    let cleanUrl = url.trim();
    while (cleanUrl.endsWith('/')) {
      cleanUrl = cleanUrl.slice(0, -1);
    }
    
    // 检查是否为有效URL
    try {
      new URL(cleanUrl);
    } catch (e) {
      // 如果不是有效URL，尝试添加https协议
      if (!cleanUrl.startsWith('http://') && !cleanUrl.startsWith('https://')) {
        cleanUrl = 'https://' + cleanUrl;
      }
    }
    
    return cleanUrl;
  } catch (e) {
    console.error('清理URL时出错:', e);
    return url;
  }
}

// 保存云同步配置
const saveSyncConfig = () => {
  localStorage.setItem(SYNC_CONFIG_KEY, JSON.stringify(syncConfig.value));
  // 单独保存S3配置到localStorage
  localStorage.setItem('syncEnabled', syncConfig.value.syncEnabled.toString());
  localStorage.setItem('s3Endpoint', syncConfig.value.s3Endpoint || '');
  localStorage.setItem('s3Region', syncConfig.value.s3Region || '');
  localStorage.setItem('s3AccessKeyId', syncConfig.value.s3AccessKeyId || '');
  localStorage.setItem('s3SecretAccessKey', syncConfig.value.s3SecretAccessKey || '');
  localStorage.setItem('s3Bucket', syncConfig.value.s3Bucket || '');
}

// 云同步配置变更处理
const handleSyncConfigChange = () => {
  // 清理endpoint URL
  syncConfig.value.s3Endpoint = cleanEndpointUrl(syncConfig.value.s3Endpoint);
  
  // 保存到localStorage
  saveSyncConfig();
}

// 手动同步功能 - 实现双向同步
const manualSync = async () => {
  try {
    syncLoading.value = true;
    
    // 检查S3配置是否完整
    if (!syncConfig.value.s3Endpoint || !syncConfig.value.s3Region || 
        !syncConfig.value.s3AccessKeyId || !syncConfig.value.s3SecretAccessKey || 
        !syncConfig.value.s3Bucket) {
      NotifyPlugin.warning({
        title: '配置不完整',
        content: '请先完成S3存储的配置',
        placement: 'top-left',
        duration: 3000
      });
      syncLoading.value = false;
      return;
    }
    
    // 确保endpoint干净，不包含存储桶名称
    syncConfig.value.s3Endpoint = cleanEndpointUrl(syncConfig.value.s3Endpoint);
    
    // 初始化S3客户端
    const s3Client = new S3Client({
      region: syncConfig.value.s3Region,
      endpoint: syncConfig.value.s3Endpoint,
      credentials: {
        accessKeyId: syncConfig.value.s3AccessKeyId,
        secretAccessKey: syncConfig.value.s3SecretAccessKey
      },
      forcePathStyle: true
    });
    
    console.log('检查S3中是否存在config.json...');
    
    // 先检查config.json是否存在
    let cloudConfig = null;
    let shouldFullBackup = false;
    
    try {
      // 使用ListObjectsV2Command检查文件是否存在
      const listCommand = new ListObjectsV2Command({
        Bucket: syncConfig.value.s3Bucket,
        Prefix: 'config.json',
        MaxKeys: 1
      });
      
      const listResponse = await s3Client.send(listCommand);
      
      // 检查是否找到config.json
      if (listResponse.Contents && listResponse.Contents.length > 0 && 
          listResponse.Contents[0].Key === 'config.json') {
        console.log('找到config.json，准备下载...');
        
        // 存在config.json，下载它
        const getConfigCommand = new GetObjectCommand({
          Bucket: syncConfig.value.s3Bucket,
          Key: 'config.json'
        });
        
        const configResponse = await s3Client.send(getConfigCommand);
        
        // 读取响应流 - 使用SDK提供的方法处理
        const configBuffer = await configResponse.Body.transformToString();
        
        // 解析config数据
        cloudConfig = JSON.parse(configBuffer);
        console.log('成功从S3下载config.json');
        
        // 处理service_config配置的合并
        const localServiceConfig = await safeGetConfig('service_config', defaultConfig);
        
        // 确保cloudConfig中有service_config对象
        if (!cloudConfig.service_config) {
          cloudConfig.service_config = {};
        }
        
        // 对于service_config的token和model键值：
        // 如果本地非空而云端为空，则使用本地值
        // 如果云端非空，则保留云端值
        if (localServiceConfig.token && !cloudConfig.service_config.token) {
          console.log('本地token非空而云端为空，使用本地值');
          cloudConfig.service_config.token = localServiceConfig.token;
        }
        
        if (localServiceConfig.model && !cloudConfig.service_config.model) {
          console.log('本地model非空而云端为空，使用本地值');
          cloudConfig.service_config.model = localServiceConfig.model;
        }
        
        // 更新本地UI界面的值
        serviceConfig.value = { ...cloudConfig.service_config };
        
        // 保存到本地IndexedDB
        await safePutConfig('service_config', serviceConfig.value);
        // 同时更新localStorage（兼容旧版本）
        localStorage.setItem(STORAGE_KEY, JSON.stringify(serviceConfig.value));
        console.log('已合并本地和云端AI服务配置');
      } else {
        console.log('S3中不存在config.json，将执行全量上传');
        shouldFullBackup = true;
      }
    } catch (configError) {
      console.warn('检查或下载config.json时出错，执行全量上传:', configError);
      shouldFullBackup = true;
    }
    
    // 如果云端没有config.json，执行全量上传
    if (shouldFullBackup) {
      console.log('执行全量上传...');
      try {
        const backupResult = await backupToS3();
        if (!backupResult) {
          // 备份失败
          syncError.value = true;
          // 即使失败也保持同步按钮显示
          hasPendingSync.value = true;
          throw new Error('备份到S3失败');
        }
        // 备份成功
        syncError.value = false;
      } catch (error) {
        console.error('全量上传失败:', error);
        syncError.value = true;
        // 即使失败也保持同步按钮显示
        hasPendingSync.value = true;
        throw error; // 重新抛出错误，以便上层处理
      }
      syncLoading.value = false;
      return;
    }
    
    // 如果云端有config.json，执行双向同步
    console.log('执行双向同步...');
    
    // 获取本地试卷更新时间
    const localPaperUpdateTimes = await safeGetConfig('papers_update_time', {});
    
    // 获取云端试卷更新时间
    let cloudPaperUpdateTimes = {};
    if (cloudConfig.papers_update_time) {
      cloudPaperUpdateTimes = cloudConfig.papers_update_time;
    }
    
    // 打印出详细的比对信息，包含完整的本地和云端数据
    console.log('================ 同步比对详情 =================');
    console.log('本地papers_update_time:', JSON.stringify(localPaperUpdateTimes, null, 2));
    console.log('云端papers_update_time:', JSON.stringify(cloudPaperUpdateTimes, null, 2));
    
    // 合并所有试卷ID（本地和云端）
    const allPaperIds = [...new Set([
      ...Object.keys(localPaperUpdateTimes),
      ...Object.keys(cloudPaperUpdateTimes)
    ])];
    console.log('合并后的所有试卷ID:', allPaperIds);
    
    // 记录需要从云端下载和上传到云端的试卷
    const papersToDownload = [];
    const papersToUpload = [];
    const papersUnchanged = [];
    
    // 比较每个试卷的更新时间
    for (const paperId of allPaperIds) {
      const localTime = localPaperUpdateTimes[paperId] || 0; // 默认为0
      const cloudTime = cloudPaperUpdateTimes[paperId] || 0; // 默认为0
      
      if (cloudTime > localTime) {
        // 云端版本更新，需要下载
        papersToDownload.push(paperId);
        console.log(`试卷 ${paperId} 云端版本更新，需要下载`);
      } else if (localTime > cloudTime) {
        // 本地版本更新，需要上传
        papersToUpload.push(paperId);
        console.log(`试卷 ${paperId} 本地版本更新，需要上传`);
      } else {
        // 版本相同，不需要同步
        papersUnchanged.push(paperId);
        console.log(`试卷 ${paperId} 版本相同，不需要同步`);
      }
    }
    
    // 下载云端更新的试卷
    for (const paperId of papersToDownload) {
      try {
        const paperFileName = `paper_${paperId}.json`;
        
        // 获取试卷数据
        const getPaperCommand = new GetObjectCommand({
          Bucket: syncConfig.value.s3Bucket,
          Key: paperFileName
        });
        
        const paperResponse = await s3Client.send(getPaperCommand);
        
        // 读取响应流 - 使用SDK提供的方法处理
        const paperBuffer = await paperResponse.Body.transformToString();
        
        // 解析试卷数据
        const paperData = JSON.parse(paperBuffer);
        
        // 更新本地数据库
        await updatePaperFromBackup(paperId, paperData);
        
        // 更新本地试卷更新时间
        localPaperUpdateTimes[paperId] = cloudPaperUpdateTimes[paperId];
        
        console.log(`试卷 ${paperId} 从云端下载成功`);
      } catch (downloadError) {
        console.error(`试卷 ${paperId} 从云端下载失败:`, downloadError);
      }
    }
    
    // 上传本地更新的试卷
    for (const paperId of papersToUpload) {
      try {
        // 获取试卷数据
        const paperStoreName = getPaperStoreName(paperId);
        const db = await initDB();
        
        // 检查存储区是否存在
        if (db.objectStoreNames.contains(paperStoreName)) {
          // 获取试卷信息
          const papersMap = await safeGetConfig('papers', {});
          const paperName = papersMap[paperId];
          
          // 获取试卷中的所有题目
          const tx = db.transaction(paperStoreName, 'readonly');
          const questions = await tx.store.getAll();
          await tx.done;
          
          // 构建试卷备份数据
          const paperBackup = {
            timestamp: Date.now(),
            paper: {
              id: paperId,
              name: paperName
            },
            questions: questions
          };
          
          // 创建试卷文件名
          const paperFileName = `paper_${paperId}.json`;
          
          // 上传试卷数据
          const paperUploadParams = {
            Bucket: syncConfig.value.s3Bucket,
            Key: paperFileName,
            Body: JSON.stringify(paperBackup),
            ContentType: 'application/json'
          };
          
          const paperCommand = new PutObjectCommand(paperUploadParams);
          await s3Client.send(paperCommand);
          
          // 更新云端试卷更新时间
          cloudPaperUpdateTimes[paperId] = localPaperUpdateTimes[paperId];
          
          console.log(`试卷 ${paperId} 上传到云端成功`);
        } else {
          console.warn(`试卷存储区 ${paperStoreName} 不存在，跳过上传`);
        }
      } catch (uploadError) {
        console.error(`试卷 ${paperId} 上传到云端失败:`, uploadError);
      }
    }
    
    // 合并papers_update_time，取每个试卷的最新时间戳，确保本地与云端一致
    const mergedUpdateTimes = {};
    for (const paperId of allPaperIds) {
      const localTime = localPaperUpdateTimes[paperId] || 0;
      const cloudTime = cloudPaperUpdateTimes[paperId] || 0;
      // 使用本地与云端中时间戳最近的一方，保证一致性
      mergedUpdateTimes[paperId] = Math.max(localTime, cloudTime);
    }
    
    // 保存合并后的时间戳到本地，稍后也会上传到云端
    await safePutConfig('papers_update_time', mergedUpdateTimes);
    
    // 获取本地试卷映射和服务配置
    const papersMap = await safeGetConfig('papers', {});
    const serviceConfigData = await safeGetConfig('service_config', defaultConfig);
    
    // 构建并上传最终的config.json
    try {
      const finalConfigBackup = {
        timestamp: Date.now(),
        service_config: serviceConfigData,
        papers: papersMap,
        papers_update_time: mergedUpdateTimes
      };
      
      const configUploadParams = {
        Bucket: syncConfig.value.s3Bucket,
        Key: 'config.json',
        Body: JSON.stringify(finalConfigBackup),
        ContentType: 'application/json'
      };
      
      const configCommand = new PutObjectCommand(configUploadParams);
      await s3Client.send(configCommand);
      console.log('最终config.json上传成功');
    } catch (finalConfigError) {
      console.error('上传最终config.json失败:', finalConfigError);
    }
    
    // 刷新UI - 只有当有需要从云端下载的试卷并且其中包含当前打开的试卷时才刷新DOM
    if (currentPaper.value && papersToDownload.includes(currentPaper.value.id)) {
      console.log(`当前试卷(${currentPaper.value.id})在下载列表中，需要刷新DOM`);
      currentQuestions.value = await loadQuestions(currentPaper.value.id);
    } else if (papersToDownload.length > 0) {
      console.log('下载的试卷与当前打开的试卷不同，无需刷新DOM');
    }
    
    // 同步完成，重置错误标志
    syncError.value = false;
    
    console.log('双向同步完成');
    } catch (error) {
      console.error('手动同步失败:', error);
      // 设置同步错误状态
      syncError.value = true;
      NotifyPlugin.error({
        title: '同步失败',
        content: error.message,
        placement: 'top-left',
        duration: 3000
      });
  } finally {
    syncLoading.value = false;
    
    // 特殊处理来自悬浮工具栏的同步请求
    // 检查调用堆栈，看是否来自floatingToolbarSync
    const callStackContainsToolbarSync = (new Error()).stack.includes('floatingToolbarSync');
    
    // 如果是从悬浮工具栏同步且出错，保持hasPendingSync为true
    if (callStackContainsToolbarSync && syncError.value) {
      console.log('保持同步按钮显示，因为是从悬浮工具栏同步且发生错误');
    }
    // 只有在没有错误的情况下才重置待同步标志
    else if (!syncError.value) {
      hasPendingSync.value = false;
      console.log('重置同步标志 hasPendingSync 为 false (manualSync finally 块)');
    } else {
      console.log('保持同步按钮显示，因为同步发生错误');
    }
  }
};

// 静默同步函数 - 执行与manualSync相同的操作但不显示弹窗
const silentSync = async () => {
  try {
    // 如果同步未启用或正在同步中，则跳过
    if (!syncConfig.value.syncEnabled || syncLoading.value) {
      console.log(`跳过同步：同步未启用或正在同步中 (enabled=${syncConfig.value.syncEnabled}, loading=${syncLoading.value})`);
      hasPendingSync.value = false;
      console.log('重置同步标志 hasPendingSync 为 false (跳过同步)');
      return;
    }
    
    console.log('开始执行静默同步...');
    syncLoading.value = true;
    
    // 检查S3配置是否完整
    if (!syncConfig.value.s3Endpoint || !syncConfig.value.s3Region || 
        !syncConfig.value.s3AccessKeyId || !syncConfig.value.s3SecretAccessKey || 
        !syncConfig.value.s3Bucket) {
      console.warn('S3配置不完整，无法执行静默同步');
      syncLoading.value = false;
      hasPendingSync.value = false;
      console.log('重置同步标志 hasPendingSync 为 false (S3配置不完整)');
      return;
    }
    
    // 确保endpoint干净，不包含存储桶名称
    syncConfig.value.s3Endpoint = cleanEndpointUrl(syncConfig.value.s3Endpoint);
    
    // 初始化S3客户端
    const s3Client = new S3Client({
      region: syncConfig.value.s3Region,
      endpoint: syncConfig.value.s3Endpoint,
      credentials: {
        accessKeyId: syncConfig.value.s3AccessKeyId,
        secretAccessKey: syncConfig.value.s3SecretAccessKey
      },
      forcePathStyle: true
    });
    
    console.log('检查S3中是否存在config.json...');
    
    // 先检查config.json是否存在
    let cloudConfig = null;
    let shouldFullBackup = false;
    
    try {
      // 使用ListObjectsV2Command检查文件是否存在
      const listCommand = new ListObjectsV2Command({
        Bucket: syncConfig.value.s3Bucket,
        Prefix: 'config.json',
        MaxKeys: 1
      });
      
      const listResponse = await s3Client.send(listCommand);
      
      // 检查是否找到config.json
      if (listResponse.Contents && listResponse.Contents.length > 0 && 
          listResponse.Contents[0].Key === 'config.json') {
        console.log('找到config.json，准备下载...');
        
        // 存在config.json，下载它
        const getConfigCommand = new GetObjectCommand({
          Bucket: syncConfig.value.s3Bucket,
          Key: 'config.json'
        });
        
        const configResponse = await s3Client.send(getConfigCommand);
        
        // 读取响应流 - 使用SDK提供的方法处理
        const configBuffer = await configResponse.Body.transformToString();
        
        // 解析config数据
        cloudConfig = JSON.parse(configBuffer);
        console.log('成功从S3下载config.json');
        
        // 处理service_config配置的合并
        const localServiceConfig = await safeGetConfig('service_config', defaultConfig);
        
        // 确保cloudConfig中有service_config对象
        if (!cloudConfig.service_config) {
          cloudConfig.service_config = {};
        }
        
        // 对于service_config的token和model键值：
        // 如果本地非空而云端为空，则使用本地值
        // 如果云端非空，则保留云端值
        if (localServiceConfig.token && !cloudConfig.service_config.token) {
          console.log('本地token非空而云端为空，使用本地值');
          cloudConfig.service_config.token = localServiceConfig.token;
        }
        
        if (localServiceConfig.model && !cloudConfig.service_config.model) {
          console.log('本地model非空而云端为空，使用本地值');
          cloudConfig.service_config.model = localServiceConfig.model;
        }
        
        // 更新本地UI界面的值
        serviceConfig.value = { ...cloudConfig.service_config };
        
        // 保存到本地IndexedDB
        await safePutConfig('service_config', serviceConfig.value);
        // 同时更新localStorage（兼容旧版本）
        localStorage.setItem(STORAGE_KEY, JSON.stringify(serviceConfig.value));
        console.log('已合并本地和云端AI服务配置');
      } else {
        console.log('S3中不存在config.json，将执行全量上传');
        shouldFullBackup = true;
      }
    } catch (configError) {
      console.warn('检查或下载config.json时出错，执行全量上传:', configError);
      shouldFullBackup = true;
    }
    
    // 如果云端没有config.json，执行全量上传
    if (shouldFullBackup) {
      console.log('执行全量上传...');
      try {
        const backupResult = await backupToS3();
        if (!backupResult) {
          // 备份失败
          syncError.value = true;
          // 即使失败也保持同步按钮显示
          hasPendingSync.value = true;
          throw new Error('备份到S3失败');
        }
        // 备份成功
        console.log('全量上传完成');
        syncError.value = false;
      } catch (error) {
        console.error('静默同步全量上传失败:', error);
        syncError.value = true;
        // 即使失败也保持同步按钮显示
        hasPendingSync.value = true;
        throw error; // 重新抛出错误，以便上层处理
      }
      syncLoading.value = false;
      // 只有成功时才重置hasPendingSync
      if (!syncError.value) {
        hasPendingSync.value = false;
        console.log('重置同步标志 hasPendingSync 为 false (全量上传完成)');
      }
      return;
    }
    
    // 如果云端有config.json，执行双向同步
    console.log('执行双向同步...');
    
    // 获取本地试卷更新时间
    const localPaperUpdateTimes = await safeGetConfig('papers_update_time', {});
    
    // 获取云端试卷更新时间
    let cloudPaperUpdateTimes = {};
    if (cloudConfig.papers_update_time) {
      cloudPaperUpdateTimes = cloudConfig.papers_update_time;
    }
    
    // 合并所有试卷ID（本地和云端）
    const allPaperIds = [...new Set([
      ...Object.keys(localPaperUpdateTimes),
      ...Object.keys(cloudPaperUpdateTimes)
    ])];
    
    // 记录需要从云端下载和上传到云端的试卷
    const papersToDownload = [];
    const papersToUpload = [];
    const papersUnchanged = [];
    
    // 比较每个试卷的更新时间
    console.log('================ 试卷同步比对结果 =================');
    for (const paperId of allPaperIds) {
      const localTime = localPaperUpdateTimes[paperId] || 0;
      const cloudTime = cloudPaperUpdateTimes[paperId] || 0;
      
      // 格式化时间戳为可读的日期时间
      const formatTime = (timestamp) => {
        if (!timestamp) return '不存在';
        const date = new Date(timestamp);
        return `${date.toLocaleDateString()} ${date.toLocaleTimeString()} [${timestamp}]`;
      };
      
      const compareResult = localTime === cloudTime ? '相同' :
                           localTime > cloudTime ? '本地较新' : '云端较新';
      
      console.log(`试卷ID: ${paperId}, 比对结果: ${compareResult}`);
      console.log(`  - 本地时间: ${formatTime(localTime)}`);
      console.log(`  - 云端时间: ${formatTime(cloudTime)}`);
      
      if (cloudTime > localTime) {
        papersToDownload.push(paperId);
        console.log(`  => 需要从云端下载`);
      } else if (localTime > cloudTime) {
        papersToUpload.push(paperId);
        console.log(`  => 需要上传到云端`);
      } else {
        papersUnchanged.push(paperId);
        console.log(`  => 无需同步`);
      }
    }
    
    // 下载云端更新的试卷
    for (const paperId of papersToDownload) {
      try {
        const paperFileName = `paper_${paperId}.json`;
        
        const getPaperCommand = new GetObjectCommand({
          Bucket: syncConfig.value.s3Bucket,
          Key: paperFileName
        });
        
        const paperResponse = await s3Client.send(getPaperCommand);
        const paperBuffer = await paperResponse.Body.transformToString();
        const paperData = JSON.parse(paperBuffer);
        
        await updatePaperFromBackup(paperId, paperData);
        localPaperUpdateTimes[paperId] = cloudPaperUpdateTimes[paperId];
        
        console.log(`试卷 ${paperId} 从云端下载成功`);
      } catch (downloadError) {
        console.error(`试卷 ${paperId} 从云端下载失败:`, downloadError);
      }
    }
    
    // 上传本地更新的试卷
    for (const paperId of papersToUpload) {
      try {
        const paperStoreName = getPaperStoreName(paperId);
        const db = await initDB();
        
        if (db.objectStoreNames.contains(paperStoreName)) {
          const papersMap = await safeGetConfig('papers', {});
          const paperName = papersMap[paperId];
          
          const tx = db.transaction(paperStoreName, 'readonly');
          const questions = await tx.store.getAll();
          await tx.done;
          
          const paperBackup = {
            timestamp: Date.now(),
            paper: {
              id: paperId,
              name: paperName
            },
            questions: questions
          };
          
          const paperFileName = `paper_${paperId}.json`;
          
          const paperUploadParams = {
            Bucket: syncConfig.value.s3Bucket,
            Key: paperFileName,
            Body: JSON.stringify(paperBackup),
            ContentType: 'application/json'
          };
          
          const paperCommand = new PutObjectCommand(paperUploadParams);
          await s3Client.send(paperCommand);
          
          cloudPaperUpdateTimes[paperId] = localPaperUpdateTimes[paperId];
          
          console.log(`试卷 ${paperId} 上传到云端成功`);
        }
      } catch (uploadError) {
        console.error(`试卷 ${paperId} 上传到云端失败:`, uploadError);
      }
    }
    
    // 合并papers_update_time，取每个试卷的最新时间戳，确保本地与云端一致
    const mergedUpdateTimes = {};
    for (const paperId of allPaperIds) {
      const localTime = localPaperUpdateTimes[paperId] || 0;
      const cloudTime = cloudPaperUpdateTimes[paperId] || 0;
      // 使用本地与云端中时间戳最近的一方，保证一致性
      mergedUpdateTimes[paperId] = Math.max(localTime, cloudTime);
    }
    
    // 保存合并后的时间戳到本地，稍后也会上传到云端
    await safePutConfig('papers_update_time', mergedUpdateTimes);
    
    // 获取本地试卷映射和服务配置
    const papersMap = await safeGetConfig('papers', {});
    const serviceConfigData = await safeGetConfig('service_config', defaultConfig);
    
    // 构建并上传最终的config.json
    try {
      const finalConfigBackup = {
        timestamp: Date.now(),
        service_config: serviceConfigData,
        papers: papersMap,
        papers_update_time: mergedUpdateTimes
      };
      
      const configUploadParams = {
        Bucket: syncConfig.value.s3Bucket,
        Key: 'config.json',
        Body: JSON.stringify(finalConfigBackup),
        ContentType: 'application/json'
      };
      
      const configCommand = new PutObjectCommand(configUploadParams);
      await s3Client.send(configCommand);
      console.log('最终config.json上传成功');
    } catch (finalConfigError) {
      console.error('上传最终config.json失败:', finalConfigError);
    }
    
    // 刷新UI - 只有当有需要从云端下载的试卷并且其中包含当前打开的试卷时才刷新DOM
    if (currentPaper.value && papersToDownload.includes(currentPaper.value.id)) {
      console.log(`当前试卷(${currentPaper.value.id})在下载列表中，需要刷新DOM`);
      currentQuestions.value = await loadQuestions(currentPaper.value.id);
    } else if (papersToDownload.length > 0) {
      console.log('下载的试卷与当前打开的试卷不同，无需刷新DOM');
    }
    
    // 打印同步结果摘要
    console.log('================ 同步结果摘要 =================');
    console.log(`从云端下载: ${papersToDownload.length} 个试卷`);
    console.log(`上传到云端: ${papersToUpload.length} 个试卷`);
    console.log(`无需同步的: ${papersUnchanged.length} 个试卷`);
    
    // 检查本地试卷是否在config.json中存在，如果不存在则删除
    console.log('检查并删除本地存在但config.json中不存在的试卷...');
    try {
      // 获取本地试卷列表
      const localPapers = await loadPapers();
      const cloudPaperIds = new Set([...Object.keys(cloudPaperUpdateTimes), ...Object.keys(cloudConfig.papers || {})]);
      
      // 记录要删除的试卷
      const papersToDelete = [];
      
      // 查找在本地存在但在云端不存在的试卷
      for (const paper of localPapers) {
        if (!cloudPaperIds.has(paper.id)) {
          papersToDelete.push(paper);
          console.log(`发现需要删除的本地试卷: ID=${paper.id}, 名称=${paper.name}`);
        }
      }
      
      // 删除不在云端的本地试卷
      for (const paper of papersToDelete) {
        try {
          console.log(`开始删除本地试卷: ID=${paper.id}, 名称=${paper.name}`);
          
          // 删除对应的试卷存储区
          const paperStoreName = getPaperStoreName(paper.id);
          const deleteVersion = currentDBVersion + 1;
          
          // 更新版本号并删除试卷存储区
          await openDB(dbName, deleteVersion, {
            upgrade(db, oldVersion, newVersion, transaction) {
              console.log(`删除试卷存储区 ${paperStoreName}，版本从 ${oldVersion} 升级到 ${newVersion}`);
              if (db.objectStoreNames.contains(paperStoreName)) {
                db.deleteObjectStore(paperStoreName);
              }
            }
          }).then(db => db.close());
          
          // 更新当前版本号
          currentDBVersion = deleteVersion;
          localStorage.setItem('dbVersion', currentDBVersion.toString());
          
          // 从配置中删除试卷信息
          const papersMap = await safeGetConfig('papers', {});
          delete papersMap[paper.id];
          await safePutConfig('papers', papersMap);
          
          // 从配置中删除试卷的更新时间记录
          const updateTimesMap = await safeGetConfig('papers_update_time', {});
          delete updateTimesMap[paper.id];
          await safePutConfig('papers_update_time', updateTimesMap);
          
          // 更新本地状态
          const paperIndex = papers.value.findIndex(p => p.id === paper.id);
          if (paperIndex !== -1) {
            papers.value.splice(paperIndex, 1);
          }
          
          // 如果删除的是当前显示的试卷，切换到其他试卷
          if (currentPaper.value && currentPaper.value.id === paper.id) {
            if (papers.value.length > 0) {
              currentPaper.value = papers.value[0];
              currentQuestions.value = await loadQuestions(currentPaper.value.id);
              // 更新保存的试卷ID
              localStorage.setItem(CURRENT_PAPER_KEY, currentPaper.value.id);
            } else {
              currentPaper.value = null;
              currentQuestions.value = [];
              localStorage.removeItem(CURRENT_PAPER_KEY);
            }
          }
          
          console.log(`成功删除本地试卷: ID=${paper.id}, 名称=${paper.name}`);
        } catch (deleteError) {
          console.error(`删除试卷 ${paper.id} 失败:`, deleteError);
        }
      }
      
      console.log(`检查完成，已删除 ${papersToDelete.length} 个本地试卷`);
    } catch (cleanupError) {
      console.error('清理本地试卷时出错:', cleanupError);
    }
    
    // 同步完成，重置错误标志
    syncError.value = false;
    console.log('静默同步完成');
  } catch (error) {
    console.error('静默同步失败:', error);
    // 设置同步错误状态
    syncError.value = true;
    // 显示错误通知
    NotifyPlugin.error({
      title: '同步失败',
      content: error.message,
      placement: 'top-left',
      duration: 3000
    });
  } finally {
    syncLoading.value = false;
    // 只有在没有错误的情况下才重置待同步标志
    if (!syncError.value) {
      hasPendingSync.value = false;
      console.log('重置同步标志 hasPendingSync 为 false (silentSync finally 块)');
    } else {
      console.log('保持同步按钮显示，因为同步发生错误');
    }
  }
};

// 存储延时同步的定时器
let delayedSyncTimer = null;
// 标记是否有待同步的数据
const hasPendingSync = ref(false);
// 标记同步是否发生错误
const syncError = ref(false);

// 当数据变更时调度延迟同步的函数
const scheduleDelayedSync = () => {
  // 如果同步未启用，跳过
  if (!syncConfig.value.syncEnabled) return;
  
  console.log('[Log] 检测到数据变更，安排2分钟后进行静默同步');
  // 设置待同步标志
  hasPendingSync.value = true;
  console.log('设置同步标志 hasPendingSync 为 true (检测到数据变更)');
  
  // 清除之前的定时器（如果存在）
  if (delayedSyncTimer) {
    clearTimeout(delayedSyncTimer);
  }
  
  // 设置新的定时器，2分钟后执行
  delayedSyncTimer = setTimeout(() => {
    console.log('执行延迟的静默同步');
    silentSync();
  }, 2 * 60 * 1000); // 2分钟
};

// 重置浮动工具栏大小的函数
const resetStickyToolSize = () => {
  if (stickyToolRef.value && stickyToolRef.value.$el) {
    // 先保存当前位置
    const el = stickyToolRef.value.$el;
    const rect = el.getBoundingClientRect();
    const currentPosition = {
      left: rect.left,
      top: rect.top,
      position: window.getComputedStyle(el).position
    };
    
    // 临时添加transition属性使布局变化平滑
    el.style.transition = 'none';
    
    // 触发重绘和尺寸重新计算
    el.style.display = 'none';
    // 强制触发重绘
    void el.offsetHeight;
    el.style.display = '';
    
    // 恢复位置
    if (currentPosition.position === 'fixed') {
      el.style.position = 'fixed';
      el.style.left = `${currentPosition.left}px`;
      el.style.top = `${currentPosition.top}px`;
      el.style.right = 'auto';
      el.style.bottom = 'auto';
    }
  
    // 使用ResizeObserver监听工具栏大小变化
    const resizeObserver = new ResizeObserver((entries) => {
      for (const entry of entries) {
        // 每次大小变化都检查位置
        checkToolbarPosition();
        console.log('工具栏大小变化，重新检查位置');
        
        // 确保恢复正确的位置
        if (currentPosition.position === 'fixed') {
          el.style.position = 'fixed';
          el.style.left = `${currentPosition.left}px`;
          el.style.top = `${currentPosition.top}px`;
          el.style.right = 'auto';
          el.style.bottom = 'auto';
        }
      }
      // 只监听一次变化后解除观察
      resizeObserver.disconnect();
    });
    
    // 开始观察大小变化
    resizeObserver.observe(el);
    
    console.log('重置浮动工具栏大小和位置');
    
    // 调用TDesign组件内部的更新方法
      stickyToolRef.value.$forceUpdate();
      
    // 额外确保工具栏位置正确
      checkToolbarPosition();
      
    // 在组件更新后再次确认位置
    nextTick(() => {
      if (currentPosition.position === 'fixed') {
        el.style.position = 'fixed';
        el.style.left = `${currentPosition.left}px`;
        el.style.top = `${currentPosition.top}px`;
        el.style.right = 'auto';
        el.style.bottom = 'auto';
      }
    });
    }
};

// 监视 hasPendingSync 的变化
watch(hasPendingSync, (newValue) => {
  console.log(`[监听] hasPendingSync 值变化为: ${newValue}`);
  
  // 使用延迟执行，确保DOM已经更新
  nextTick(() => {
    setTimeout(() => {
      resetStickyToolSize();
      
      // 如果同步按钮出现，添加触摸事件
      if (newValue) {
        setupSyncButtonTouchEvent();
      }
    }, 50); // 小延迟确保DOM已更新
  });
});

// 监听papers_update_time的变更并在2分钟后触发同步
const setupUpdateTimeChangeMonitor = () => {
  // 记录上次观察到的数据状态
  const lastState = {
    questionsHash: '',
    papersIds: []
  };
  
  // 计算题目数据的哈希值，用于比较是否发生实际变更
  const calculateQuestionsHash = (questions) => {
    if (!questions || questions.length === 0) return '';
    // 只关注可能导致需要同步的关键数据变化
    const simplifiedQuestions = questions.map(q => ({
      id: q.id,
      detail: q.detail, 
      answer: q.answer,
      simpleAnswer: q.simpleAnswer
      // 不包括stars和showAnalysis等UI状态属性
    }));
    return JSON.stringify(simplifiedQuestions);
  };
  
  // 获取试卷ID列表
  const getPaperIds = (papersList) => {
    return papersList.map(p => p.id).sort();
  };
  
  // 使用事件监听的方式来检测数据更改
  // 每次修改题目时，检查是否为真正的数据变更，再触发同步
  watch(currentQuestions, (newQuestions) => {
    if (newQuestions && newQuestions.length > 0) {
      const newHash = calculateQuestionsHash(newQuestions);
      // 只有当哈希值发生变化时才认为是真正的数据变更
      if (lastState.questionsHash && lastState.questionsHash !== newHash) {
        console.log('检测到题目内容实际变更，安排同步');
        scheduleDelayedSync();
      }
      lastState.questionsHash = newHash;
    }
  }, { deep: true });
  
  // 监听试卷列表变化
  watch(papers, (newPapers) => {
    if (newPapers && newPapers.length > 0) {
      const newPaperIds = getPaperIds(newPapers);
      // 比较试卷ID列表是否发生变化
      const papersChanged = lastState.papersIds.length !== newPaperIds.length || 
        JSON.stringify(lastState.papersIds) !== JSON.stringify(newPaperIds);
      
      if (lastState.papersIds.length > 0 && papersChanged) {
        console.log('检测到试卷列表实际变更，安排同步');
        scheduleDelayedSync();
      }
      lastState.papersIds = newPaperIds;
    }
  }, { deep: true });
};

// 处理同步按钮状态切换
const handleSyncToggle = async () => {
  // 确保endpoint干净，不包含存储桶名称
  syncConfig.value.s3Endpoint = cleanEndpointUrl(syncConfig.value.s3Endpoint);
  
  // 检查S3配置是否完整
  if (!syncConfig.value.s3Endpoint || !syncConfig.value.s3Region || 
      !syncConfig.value.s3AccessKeyId || !syncConfig.value.s3SecretAccessKey || 
      !syncConfig.value.s3Bucket) {
    // 如果未设置S3配置，显示TDesign提示
    NotifyPlugin.warning({
      title: '配置不完整',
      content: '请先完成S3存储的配置',
      placement: 'top-left',
      duration: 3000
    });
    syncConfig.value.syncEnabled = false;
    return;
  }
  
  // 切换同步状态
  syncConfig.value.syncEnabled = !syncConfig.value.syncEnabled;
  
  // 保存到localStorage
  localStorage.setItem('syncEnabled', syncConfig.value.syncEnabled.toString());
  localStorage.setItem('s3Endpoint', syncConfig.value.s3Endpoint);
  localStorage.setItem('s3Region', syncConfig.value.s3Region);
  localStorage.setItem('s3AccessKeyId', syncConfig.value.s3AccessKeyId);
  localStorage.setItem('s3SecretAccessKey', syncConfig.value.s3SecretAccessKey);
  localStorage.setItem('s3Bucket', syncConfig.value.s3Bucket);
  
  if (syncConfig.value.syncEnabled) {
    // 如果启用了同步，执行一次静默同步
    try {
      console.log('S3同步已启用，开始静默同步数据...');
      
      // 执行静默同步
      await silentSync();
      
      // 显示成功提示
      NotifyPlugin.success({
        title: '同步已启用',
        content: 'S3自动同步已成功启用',
        placement: 'top-left',
        duration: 3000
      });
      
    } catch (error) {
      console.error('启用S3同步时出错:', error);
      // 发生错误时禁用同步
      syncConfig.value.syncEnabled = false;
      localStorage.setItem('syncEnabled', 'false');
      
      // 显示错误提示
      NotifyPlugin.error({
        title: '启用失败',
        content: error.message,
        placement: 'top-left',
        duration: 3000
      });
    }
  } else {
    // 如果禁用了同步，显示提示
    NotifyPlugin.info({
      title: '同步已禁用',
      content: 'S3自动同步已关闭',
      placement: 'top-left',
      duration: 3000
    });
    console.log('S3同步已禁用');
  }
};

// 添加重建本地数据的函数
const rebuildLocalData = async (cloudData, cloudStars) => {
  try {
    console.log('开始重建本地数据');
    const db = await initDB();
    
    // 1. 处理星级评分数据 - 完全覆盖
    if (cloudStars && typeof cloudStars === 'object') {
      console.log('使用云端数据覆盖本地星级评分数据');
      const cleanStarsData = JSON.parse(JSON.stringify(cloudStars));
      await safePutConfig('stars', cleanStarsData);
    }
    
    // 2. 处理试卷数据 - 完全覆盖
    if (cloudData.papers && Array.isArray(cloudData.papers)) {
      console.log(`处理 ${cloudData.papers.length} 个云端试卷`);
      
      // 2.1 获取当前所有存储区
      const storeNames = Array.from(db.objectStoreNames);
      const paperStoreNames = storeNames.filter(name => name.startsWith('paper_'));
      
      // 2.2 关闭当前数据库连接
      db.close();
      
      // 2.3 准备新版本号
      currentDBVersion += 1;
      
      // 2.4 创建新版本数据库，删除所有旧的试卷存储区
      const newDb = await openDB(dbName, currentDBVersion, {
        upgrade(db, oldVersion, newVersion, transaction) {
          console.log(`升级数据库删除旧试卷存储区: ${oldVersion} -> ${newVersion}`);
          
          // 删除所有旧的试卷存储区
          for (const storeName of paperStoreNames) {
            if (db.objectStoreNames.contains(storeName)) {
              db.deleteObjectStore(storeName);
              console.log(`删除存储区: ${storeName}`);
            }
          }
        }
      });
      
      // 2.5 创建用于保存试卷信息的映射
      const papersMap = {};
      
      // 2.6 为每个云端试卷创建存储区
      for (const cloudPaper of cloudData.papers) {
        if (!cloudPaper.id) {
          console.warn('跳过缺少ID的试卷:', cloudPaper);
          continue;
        }
        
        // 添加到试卷映射
        papersMap[cloudPaper.id] = cloudPaper.name || '';
      }
      
      // 2.7 保存试卷映射到config表
      await newDb.put(configStoreName, {
        id: 'papers',
        data: papersMap
      });
      
      // 2.8 关闭当前连接
      newDb.close();
      
      // 2.9 需要为每个试卷创建单独的存储区，所以每个试卷都增加版本号
      for (const cloudPaper of cloudData.papers) {
        if (!cloudPaper.id) continue;
        
        currentDBVersion += 1;
        const paperDb = await openDB(dbName, currentDBVersion, {
          upgrade(db, oldVersion, newVersion, transaction) {
            console.log(`为试卷 ${cloudPaper.id} 创建存储区: ${oldVersion} -> ${newVersion}`);
            
            // 创建试卷存储区
            const paperStoreName = getPaperStoreName(cloudPaper.id);
            if (!db.objectStoreNames.contains(paperStoreName)) {
              db.createObjectStore(paperStoreName, { keyPath: 'id', autoIncrement: true });
        }
      }
        });
        
        paperDb.close();
      }
      
      // 重新打开数据库，获取最新连接
      const finalDb = await initDB();
      
      console.log(`已从云端重建 ${cloudData.papers.length} 个试卷`);
      
      // 刷新本地试卷列表
      papers.value = await loadPapers();
    }
    
    // 3. 处理题目数据 - 按试卷分别保存
    if (cloudData.questions && Array.isArray(cloudData.questions) && cloudData.papers && Array.isArray(cloudData.papers)) {
      console.log(`处理 ${cloudData.questions.length} 个云端题目`);
      
      // 3.1 按试卷ID对题目进行分组
      const questionsByPaper = {};
      
      // 首先根据papers数据创建映射
      for (const paper of cloudData.papers) {
        if (paper.id && paper.questionIds && Array.isArray(paper.questionIds)) {
          // 初始化当前试卷的题目集合
          questionsByPaper[paper.id] = new Set(paper.questionIds);
        }
      }
      
      // 3.2 将题目数据分配到各自的试卷中
      const questionMap = {};
      for (const question of cloudData.questions) {
        if (question.id) {
          questionMap[question.id] = question;
        }
      }
      
      let totalAddedCount = 0;
      
      // 3.3 分别处理每个试卷中的题目
      for (const paperId in questionsByPaper) {
        const paperStoreName = getPaperStoreName(paperId);
        const questionIds = questionsByPaper[paperId];
        
        // 确保试卷存储区存在
        const db = await initDB();
        if (db.objectStoreNames.contains(paperStoreName)) {
          // 打开事务并获取存储对象
          const tx = db.transaction(paperStoreName, 'readwrite');
          const paperStore = tx.objectStore(paperStoreName);
          
          // 清空当前试卷中的所有题目
          const existingKeys = await paperStore.getAllKeys();
          for (const key of existingKeys) {
            await paperStore.delete(key);
          }
          
          // 添加题目到当前试卷
          let paperAddedCount = 0;
          for (const questionId of questionIds) {
            const cloudQuestion = questionMap[questionId];
            if (cloudQuestion) {
        // 添加新题目，但去除stars属性(如果有)
        const newQuestion = { ...cloudQuestion };
        if ('stars' in newQuestion) {
          delete newQuestion.stars;
        }
        
        // 转换为detail格式
        const convertedQuestion = convertToDetailFormat(newQuestion);
        
        try {
                await paperStore.add(JSON.parse(JSON.stringify(convertedQuestion)));
                paperAddedCount++;
        } catch (e) {
                console.error(`向试卷 ${paperId} 添加题目 ${questionId} 失败:`, e);
              }
            }
          }
          
          // 完成事务
          await tx.done;
          
          console.log(`已向试卷 ${paperId} 添加 ${paperAddedCount} 个题目`);
          totalAddedCount += paperAddedCount;
        } else {
          console.error(`试卷存储区 ${paperStoreName} 不存在，无法添加题目`);
        }
      }
      
      console.log(`已从云端重建 ${totalAddedCount} 个题目`);
    }
    
    console.log('本地数据重建完成');
  } catch (error) {
    console.error('重建本地数据失败:', error);
    throw error;
  }
};

// 从云端获取数据
const fetchCloudData = async () => {
  try {
    // 构建API URL
    const url = syncConfig.value.backendUrl;
    console.log('正在从云端获取数据，URL:', url);
    
    // 确保URL中包含问号
    const apiUrl = url.includes('?') ? `${url}&type=all` : `${url}?type=all`;
    console.log('完整API URL:', apiUrl);
    
    const response = await fetch(apiUrl, {
      method: 'GET',
      headers: {
        'Content-Type': 'application/json'
      }
    });
    
    if (!response.ok) {
      const errorText = await response.text();
      console.error('获取云端数据失败:', response.status, errorText);
      throw new Error(`获取云端数据失败: ${response.status}`);
    }
    
    const data = await response.json();
    console.log('获取到的云端数据:', {
      papers: data.papers?.length || 0,
      questions: data.questions?.length || 0
    });
    
    if (!data.papers || !Array.isArray(data.papers)) {
      console.warn('云端返回的试卷数据格式不正确:', data.papers);
    }
    
    if (!data.questions || !Array.isArray(data.questions)) {
      console.warn('云端返回的题目数据格式不正确:', data.questions);
    }
    
    return data;
  } catch (error) {
    console.error('获取云端数据异常:', error);
    throw error;
  }
};

// 合并数据到本地
const mergeDataToLocal = async (cloudData) => {
  try {
    console.log('开始合并云端数据到本地');
    const db = await initDB();
    
    // 处理云端试卷数据
    if (cloudData.papers && Array.isArray(cloudData.papers)) {
      console.log(`处理 ${cloudData.papers.length} 个云端试卷`);
      
      // 获取所有本地试卷
      const localPapers = await db.getAll(papersStoreName);
      const localPapersMap = new Map(localPapers.map(p => [p.id, p]));
      console.log(`本地已有 ${localPapers.length} 个试卷`);
      
      // 遍历云端试卷，合并到本地
      let updatedCount = 0;
      let addedCount = 0;
      for (const cloudPaper of cloudData.papers) {
        if (!cloudPaper.id) {
          console.warn('试卷缺少ID:', cloudPaper);
          continue;
        }
        
        if (localPapersMap.has(cloudPaper.id)) {
          // 更新已存在的试卷
          const localPaper = localPapersMap.get(cloudPaper.id);
          localPaper.name = cloudPaper.name;
          if (Array.isArray(cloudPaper.questionIds)) {
            localPaper.questionIds = [...new Set([...localPaper.questionIds, ...cloudPaper.questionIds])];
          }
          await db.put(papersStoreName, localPaper);
          updatedCount++;
        } else {
          // 添加新试卷
          await db.add(papersStoreName, cloudPaper);
          addedCount++;
        }
      }
      console.log(`试卷处理完成: 更新 ${updatedCount} 个, 新增 ${addedCount} 个`);
    } else {
      console.warn('无有效的云端试卷数据');
    }
    
    // 处理云端题目数据
    if (cloudData.questions && Array.isArray(cloudData.questions)) {
      console.log(`处理 ${cloudData.questions.length} 个云端题目`);
      
      // 获取所有本地题目
      const localQuestions = await db.getAll(questionsStoreName);
      const localQuestionsMap = new Map(localQuestions.map(q => [q.id, q]));
      console.log(`本地已有 ${localQuestions.length} 个题目`);
      
      // 遍历云端题目，合并到本地
      let updatedCount = 0;
      let addedCount = 0;
      for (const cloudQuestion of cloudData.questions) {
        if (!cloudQuestion.id) {
          console.warn('题目缺少ID:', cloudQuestion);
          continue;
        }
        
        if (localQuestionsMap.has(cloudQuestion.id)) {
          // 更新已存在的题目，但不包含stars属性(现在stars存储在单独的配置中)
          const localQuestion = localQuestionsMap.get(cloudQuestion.id);
          const mergedQuestion = {
            ...cloudQuestion,
            // 不再合并星级评分，它现在存储在单独的配置中
            answer: localQuestion.answer || cloudQuestion.answer || '',
            simpleAnswer: localQuestion.simpleAnswer || cloudQuestion.simpleAnswer || '',
            correctChoice: localQuestion.correctChoice || cloudQuestion.correctChoice || ''
          };
          
          // 删除可能存在的stars属性
          if ('stars' in mergedQuestion) {
            delete mergedQuestion.stars;
          }
          
          // 转换为detail格式
          const convertedQuestion = convertToDetailFormat(mergedQuestion);
          
          await db.put(questionsStoreName, convertedQuestion);
          updatedCount++;
        } else {
          // 添加新题目，但去除stars属性(如果有)
          const newQuestion = { ...cloudQuestion };
          if ('stars' in newQuestion) {
            delete newQuestion.stars;
          }
          
          // 转换为detail格式
          const convertedQuestion = convertToDetailFormat(newQuestion);
          
          await db.add(questionsStoreName, convertedQuestion);
          addedCount++;
        }
      }
      console.log(`题目处理完成: 更新 ${updatedCount} 个, 新增 ${addedCount} 个`);
    } else {
      console.warn('无有效的云端题目数据');
    }
    
    console.log('刷新本地缓存数据');
    // 刷新本地数据
    papers.value = await loadPapers();
    
    if (currentPaper.value) {
      console.log(`重新加载当前试卷(ID: ${currentPaper.value.id})的题目`);
      currentQuestions.value = await loadQuestions(currentPaper.value.id);
    }
    
    console.log('数据合并完成');
  } catch (error) {
    console.error('合并数据到本地时出错:', error);
    throw error;
  }
};

// 从云端获取配置数据
const fetchConfigFromBackend = async () => {
  try {
    if (!syncConfig.value.backendUrl) {
      return;
    }
    
    // 构建API URL
    const url = syncConfig.value.backendUrl;
    
    // 确保URL中包含问号
    const apiUrl = url.includes('?') ? `${url}&type=config` : `${url}?type=config`;
    
    const response = await fetch(apiUrl, {
      method: 'GET',
      headers: {
        'Content-Type': 'application/json'
      }
    });
    
    if (!response.ok) {
      throw new Error(`获取云端配置失败: ${response.status}`);
    }
    
    const configData = await response.json();
    
    // 更新本地配置
    if (configData && Object.keys(configData).length > 0) {
      serviceConfig.value = configData;
      
      // 保存到 IndexedDB
      const db = await initDB();
      await db.put(configStoreName, {
        id: 'service_config',
        data: configData
      });
      
      // 也更新 localStorage
      localStorage.setItem(STORAGE_KEY, JSON.stringify(configData));
      
      console.log('从云端获取配置成功');
    }
  } catch (error) {
    console.error('从云端获取配置失败:', error);
    // 这里不要抛出错误，因为配置同步失败不应该影响整体功能
  }
};

// 与云端同步配置数据
const syncConfigWithBackend = async (config) => {
  try {
    // 检查后端URL是否配置
    if (!syncConfig.value.backendUrl) {
      console.log('后端URL未配置，跳过配置同步');
      return { success: true, message: '后端已弃用，配置仅保存在本地' };
    }
    
    // 创建安全的配置对象副本
    const safeConfig = {
      token: config?.token || '',
      model: config?.model || '',
      prompt: config?.prompt || defaultConfig.prompt
    };
    
    // 构建API URL
    const url = syncConfig.value.backendUrl;
    
    // 确保URL中包含问号
    const apiUrl = url.includes('?') ? `${url}&type=config` : `${url}?type=config`;
    
    // 打印将要发送的数据，用于调试
    console.log('准备发送到云端的配置数据:', JSON.stringify(safeConfig));
    
    // 发送数据到后端
    const response = await fetch(apiUrl, {
      method: 'PUT',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify(safeConfig)
    });
    
    if (!response.ok) {
      const errorText = await response.text();
      console.error('云端响应错误:', errorText);
      throw new Error(`同步配置到云端失败: ${response.status}`);
    }
    
    console.log('配置同步到云端成功');
    return await response.json();
  } catch (error) {
    console.error('同步配置到云端失败:', error);
    // 这里不要抛出错误，因为配置同步失败不应该影响整体功能
    return { success: false, message: error.message };
  }
};

// initializeApp函数已在上方定义，此处删除重复代码

// 后台从云端同步数据的函数（非阻塞）
const syncFromCloudInBackground = async () => {
  try {
    console.log('开始后台从云端获取数据...')
    
    const db = await initDB()
    
    // 1. 从云端获取星级评分数据
    console.log('1. 从云端获取星级评分数据')
    const cloudStars = await fetchStarsFromBackend()
    
    if (cloudStars) {
      // 更新本地星级评分数据
      await db.put(configStoreName, {
        id: 'stars',
        data: cloudStars
      })
      
      // 更新当前显示的题目的星级
      await updateCurrentQuestionsStars()
    }
    
    // 2. 从云端获取试卷数据
    console.log('2. 获取试卷数据和试题ID列表');
    const cloudPapers = await fetchCloudPapers();
    if (!cloudPapers || !Array.isArray(cloudPapers)) {
      console.warn('未能获取到有效的云端试卷数据，保持使用本地数据');
      return;
    }
    
    // 3. 整理所有试题ID列表
    const allQuestionIds = new Set();
    for (const paper of cloudPapers) {
      if (paper.questionIds && Array.isArray(paper.questionIds)) {
        paper.questionIds.forEach(id => allQuestionIds.add(id));
      }
    }
    
    // 4. 获取所有现有的试卷存储区并收集题目ID
    const storeNames = Array.from(db.objectStoreNames);
    const paperStoreNames = storeNames.filter(name => name.startsWith('paper_'));
    
    const localQuestionIds = new Set();
    for (const storeName of paperStoreNames) {
      const storeQuestions = await db.getAll(storeName);
      storeQuestions.forEach(q => localQuestionIds.add(q.id));
    }
    
    const missingQuestionIds = [...allQuestionIds].filter(id => !localQuestionIds.has(id));
    console.log(`3. 发现 ${missingQuestionIds.length} 个缺少的题目，开始获取`);
    
    // 5. 按需获取缺少的题目数据
    if (missingQuestionIds.length > 0) {
      await fetchMissingQuestions(missingQuestionIds);
    }
    
    // 6. 备份当前页面状态
    const currentPaperId = currentPaper.value?.id;
    const currentScrollPosition = localStorage.getItem(SCROLL_POSITION_KEY);
    
    // 7. 清空并重建试卷数据
    console.log('4. 清空并重建试卷数据');
    
    // 7.1 获取当前papersStore的所有键，准备清空
    const paperKeys = await db.getAllKeys(papersStoreName);
    
    // 7.2 删除所有现有试卷
    const tx = db.transaction(papersStoreName, 'readwrite');
    const papersStore = tx.objectStore(papersStoreName);
    
    for (const key of paperKeys) {
      await papersStore.delete(key);
    }
    await tx.done;
    console.log(`已清空本地试卷表中的 ${paperKeys.length} 个试卷`);
    
    // 7.3 添加云端试卷数据
    for (const cloudPaper of cloudPapers) {
      // 创建一个干净的数据对象，确保可以被克隆
      const cleanPaper = {
        id: cloudPaper.id,
        name: cloudPaper.name,
        questionIds: Array.isArray(cloudPaper.questionIds) ? [...cloudPaper.questionIds] : []
      };
      
      try {
        // 直接添加，不检查是否存在（因为已经清空了）
        await db.add(papersStoreName, cleanPaper);
      } catch (error) {
        console.error(`添加试卷 ${cleanPaper.id} 失败:`, error);
      }
    }
    
    // 8. 更新星级评分数据
    if (cloudStars) {
      console.log('5. 更新星级评分数据');
      const cleanStarsData = JSON.parse(JSON.stringify(cloudStars));
      await db.put(configStoreName, {
        id: 'stars',
        data: cleanStarsData
      });
    }
    
    // 9. 重新加载试卷列表到页面
    papers.value = await loadPapers();
    console.log(`已从云端重建 ${papers.value.length} 个试卷到页面`);
    
    // 10. 恢复当前正在查看的试卷（如果存在）
    if (currentPaperId) {
      const paperToRestore = papers.value.find(p => p.id === currentPaperId);
      if (paperToRestore) {
        console.log(`保持当前正在查看的试卷: ${paperToRestore.name}`);
        currentPaper.value = paperToRestore;
        currentQuestions.value = await loadQuestions(currentPaperId);
      } else if (papers.value.length > 0) {
        // 如果之前的试卷不存在，则切换到第一个试卷
        console.log(`之前的试卷不存在，切换到: ${papers.value[0].name}`);
        currentPaper.value = papers.value[0];
        currentQuestions.value = await loadQuestions(papers.value[0].id);
        localStorage.setItem(CURRENT_PAPER_KEY, papers.value[0].id);
      }
    }
    
    console.log('云同步数据更新完成');
  } catch (error) {
    console.error('后台云同步数据更新失败:', error);
    // 失败时不影响用户体验，仅记录错误
  }
};

// 使用本地数据初始化
const initializeWithLocalData = async () => {
  // 恢复上次浏览的试卷
  const savedPaperId = localStorage.getItem(CURRENT_PAPER_KEY);
  console.log(`从localStorage获取到上次浏览的试卷ID: ${savedPaperId}`);
  
  if (savedPaperId) {
    // 将字符串ID转换为数字
    const paperId = parseInt(savedPaperId);
    const savedPaper = papers.value.find(p => p.id === paperId);
    
    if (savedPaper) {
      console.log(`找到对应的试卷: ${savedPaper.name}`);
      currentPaper.value = savedPaper;
      currentQuestions.value = await loadQuestions(savedPaper.id);
      
      // 等待数据加载完成后再恢复滚动位置
      await nextTick();
      console.log('数据加载完成，准备恢复滚动位置');
      restoreScrollPosition();
    } else {
      console.log(`未找到ID为 ${paperId} 的试卷，使用第一个试卷`);
      if (papers.value.length > 0) {
        currentPaper.value = papers.value[0];
        currentQuestions.value = await loadQuestions(currentPaper.value.id);
        // 更新保存的试卷ID
        localStorage.setItem(CURRENT_PAPER_KEY, currentPaper.value.id);
      }
    }
  } else if (papers.value.length > 0) {
    console.log('没有保存的试卷ID，使用第一个试卷');
    currentPaper.value = papers.value[0];
    currentQuestions.value = await loadQuestions(currentPaper.value.id);
    // 保存当前试卷ID
    localStorage.setItem(CURRENT_PAPER_KEY, currentPaper.value.id);
  }
};

// 获取云端试卷数据
const fetchCloudPapers = async () => {
  try {
    if (!syncConfig.value.backendUrl) {
      console.log('后端URL未配置，跳过获取云端试卷数据');
      return []; // 返回空数组而不是抛出错误
    }
    
    // 构建API URL
    const url = syncConfig.value.backendUrl;
    
    // 从云端获取试卷数据
    const papersApiUrl = url.includes('?') ? `${url}&type=papers` : `${url}?type=papers`;
    
    const response = await fetch(papersApiUrl, {
      method: 'GET',
      headers: {
        'Content-Type': 'application/json'
      }
    });
    
    if (!response.ok) {
      throw new Error(`获取云端试卷数据失败: ${response.status}`);
    }
    
    const papersData = await response.json();
    console.log(`获取到 ${papersData.length} 个云端试卷`);
    return papersData;
  } catch (error) {
    console.error('获取云端试卷数据失败:', error);
    throw error;
  }
};

// 获取缺少的题目数据
const fetchMissingQuestions = async (missingIds) => {
  try {
    if (!missingIds || missingIds.length === 0) {
      return [];
    }
    
    console.log(`开始获取 ${missingIds.length} 个缺少的题目...`);
    const db = await initDB();
    
    // 分批获取数据，每次最多获取20个
    const BATCH_SIZE = 20;
    let questionsData = [];
    
    for (let i = 0; i < missingIds.length; i += BATCH_SIZE) {
      const batchIds = missingIds.slice(i, i + BATCH_SIZE);
      console.log(`获取第 ${i}-${i + batchIds.length} 个题目`);
      
      const idsParam = batchIds.join(',');
      const url = syncConfig.value.backendUrl;
      const apiUrl = url.includes('?') ? 
        `${url}&type=questions&ids=${idsParam}` : 
        `${url}?type=questions&ids=${idsParam}`;
      
      const response = await fetch(apiUrl);
      
      if (response.ok) {
        const batchData = await response.json();
        if (Array.isArray(batchData)) {
          questionsData = [...questionsData, ...batchData];
        }
      } else {
        console.error(`获取题目批次 ${i} 失败:`, response.status);
      }
    }
    
    // 在新的数据结构中，首先需要知道每个题目属于哪个试卷
    // 从云端获取试卷数据
    const cloudPapers = await fetchCloudPapers();
    
    // 创建题目ID到试卷ID的映射
    const questionToPaper = {};
    
    if (cloudPapers && Array.isArray(cloudPapers)) {
      for (const paper of cloudPapers) {
        if (paper.id && paper.questionIds && Array.isArray(paper.questionIds)) {
          for (const qId of paper.questionIds) {
            questionToPaper[qId] = paper.id;
          }
        }
      }
    }
    
    // 处理获取到的题目数据
    if (Array.isArray(questionsData)) {
      for (const question of questionsData) {
        if (!question.id) continue;
        
        // 确定题目所属的试卷
        const paperId = questionToPaper[question.id];
        if (!paperId) {
          console.warn(`题目 ${question.id} 没有对应的试卷信息，无法保存`);
          continue;
        }
        
        // 获取试卷存储区名称
        const paperStoreName = getPaperStoreName(paperId);
        
        // 确保试卷存储区存在
        if (!db.objectStoreNames.contains(paperStoreName)) {
          console.warn(`试卷存储区 ${paperStoreName} 不存在，跳过保存题目 ${question.id}`);
          continue;
        }
        
        // 创建一个干净的数据对象，确保可以被克隆
        const cleanQuestion = {
          id: question.id,
          answer: question.answer || '',
          simpleAnswer: question.simpleAnswer || '',
          correctChoice: question.correctChoice || '',
          keywords: Array.isArray(question.keywords) ? [...question.keywords] : []
        };
        
        // 根据问题结构，保留相应的字段
        if (question.detail && Array.isArray(question.detail)) {
          // 新结构：包含detail字段
          cleanQuestion.detail = [...question.detail];
        } else if (question.title || question.options) {
          // 旧结构：包含title和options字段
          cleanQuestion.title = question.title || '';
          cleanQuestion.options = Array.isArray(question.options) ? [...question.options] : [];
        }
        
        // 删除可能存在的stars属性
        if ('stars' in cleanQuestion) {
          delete cleanQuestion.stars;
        }
        
        // 转换为detail格式
        const convertedQuestion = convertToDetailFormat(cleanQuestion);
        
        // 获取存储区事务
        const tx = db.transaction(paperStoreName, 'readwrite');
        const store = tx.objectStore(paperStoreName);
        
        // 检查题目是否已存在
        try {
          const existingQuestion = await store.get(cleanQuestion.id);
          if (existingQuestion) {
            // 更新现有题目，但保留本地答案内容（如果有）
            const mergedQuestion = {
              ...convertedQuestion,
              answer: existingQuestion.answer || convertedQuestion.answer || '',
              simpleAnswer: existingQuestion.simpleAnswer || convertedQuestion.simpleAnswer || '',
              correctChoice: existingQuestion.correctChoice || convertedQuestion.correctChoice || ''
            };
            
            // 使用 JSON 序列化和反序列化来确保对象是可克隆的
            await store.put(JSON.parse(JSON.stringify(mergedQuestion)));
          } else {
            // 添加新题目
            await store.add(convertedQuestion);
          }
          
          // 完成事务
          await tx.done;
        } catch (err) {
          console.error(`保存题目 ${cleanQuestion.id} 到试卷 ${paperId} 失败:`, err);
        }
      }
      console.log(`成功保存题目到本地数据库`);
    }
    
    return questionsData;
  } catch (error) {
    console.error('获取缺少题目失败:', error);
    return [];
  }
};

onMounted(async () => {
  // 重置滚动恢复标记，确保每次页面加载都能尝试恢复滚动
  scrollRestoreAttempted = false;
  
  // 从localStorage恢复主题模式
  const savedTheme = localStorage.getItem('theme-mode')
  if (savedTheme === 'dark') {
    isDarkMode.value = true
    document.documentElement.setAttribute('theme-mode', 'dark')
    // 设置深色主题色
    document.querySelector('meta[name="theme-color"][media="(prefers-color-scheme: dark)"]').setAttribute('content', '#242424')
    document.querySelector('meta[name="theme-color"][media="(prefers-color-scheme: light)"]').setAttribute('content', '#242424')
    
    // 加载并应用文字透明度设置
    const savedOpacity = parseInt(localStorage.getItem('text-opacity') || '30', 10)
    uiConfig.value.textOpacity = savedOpacity
    applyTextOpacity(savedOpacity)
  } else {
    isDarkMode.value = false
    document.documentElement.removeAttribute('theme-mode')
    // 设置浅色主题色
    document.querySelector('meta[name="theme-color"][media="(prefers-color-scheme: dark)"]').setAttribute('content', '#ffffff')
    document.querySelector('meta[name="theme-color"][media="(prefers-color-scheme: light)"]').setAttribute('content', '#ffffff')
  }

  // 初始化字体大小
  const savedFontSize = parseInt(localStorage.getItem('font-size') || '0', 10)
  uiConfig.value.fontSize = savedFontSize
  
  // 尝试立即应用字体大小
  applyFontSize(savedFontSize)
  
  // 确保字体大小最终会被应用（防止DOM元素尚未加载）
  ensureFontSizeApplied()

  // 监听窗口大小变化
  window.addEventListener('resize', handleResize)
  
  // 初始化应用数据
  await initializeApp();
  
  // 在应用数据加载完成后尝试恢复滚动位置
  nextTick(() => {
    // 减少延迟时间，避免与 watch 重复触发时间过近
    setTimeout(() => {
      restoreScrollPosition();
    }, 300);
  });
  
  // 初始化拖动行为
  nextTick(() => {
    if (stickyToolRef.value) {
      // 使用更准确的选择器来获取拖动句柄元素
      const dragHandle = stickyToolRef.value.$el.querySelector('.t-sticky-item.drag-handle')
      if (dragHandle) {
        dragHandle.addEventListener('mousedown', startDrag)
        dragHandle.addEventListener('touchstart', startTouchDrag, { passive: false })
        console.log('已添加拖动事件监听器')
      } else {
        // 更灵活的备选选择器
        const alternativeDragHandle = stickyToolRef.value.$el.querySelector('.drag-handle') ||
                                     stickyToolRef.value.$el.querySelector('.t-sticky-item--compact.t-sticky-item--round') ||
                                     stickyToolRef.value.$el.firstElementChild
        
        if (alternativeDragHandle) {
          alternativeDragHandle.addEventListener('mousedown', startDrag)
          alternativeDragHandle.addEventListener('touchstart', startTouchDrag, { passive: false })
          console.log('已添加拖动事件监听器（使用备选元素）')
        } else {
          console.warn('找不到任何可用的拖动句柄元素')
        }
      }
      
      // 恢复工具栏位置
      restoreToolbarPosition()
      
        // 初始化后重置工具栏大小
  setTimeout(() => {
    resetStickyToolSize();
    
    // 初始化同步按钮的触摸事件
    if (hasPendingSync.value) {
      setupSyncButtonTouchEvent();
    }
  }, 100);
} else {
  console.warn('悬浮工具栏引用不存在')
}
  })
});

// 修改 watch 监听器
watch(currentQuestions, async (newQuestions) => {
  if (newQuestions.length > 0) {
    // 在题目列表更新时恢复滚动位置
    const savedPosition = localStorage.getItem(SCROLL_POSITION_KEY)
    if (savedPosition) {
      try {
        const { paperId, questionIndex } = JSON.parse(savedPosition)
        // 移除仅切换试卷时才恢复的限制，确保刷新页面后也能恢复
        await nextTick()
        console.log('题目列表更新，尝试恢复滚动位置，试卷ID:', paperId, '当前试卷ID:', currentPaper.value?.id)
        // 等待一段时间确保 DOM 完全渲染
        setTimeout(() => {
          restoreScrollPosition()
        }, 200)
      } catch (error) {
        console.error('解析保存的滚动位置出错:', error)
      }
    }
    
    // 在题目列表更新后，确保字体大小设置正确应用
    await nextTick()
    setTimeout(() => {
      ensureFontSizeApplied()
    }, 200)
  }
}, { immediate: true })

// 监视当前试卷变化，确保在切换试卷后应用字体大小设置
watch(currentPaper, async () => {
  await nextTick()
  setTimeout(() => {
    ensureFontSizeApplied()
  }, 200)
})

// 组件卸载时移除事件监听
onUnmounted(() => {
  window.removeEventListener('resize', handleResize)
  // 使用更准确的选择器和多种备选方案确保移除事件监听
  if (stickyToolRef.value && stickyToolRef.value.$el) {
    const dragHandleSelectors = [
      '.t-sticky-item.drag-handle',
      '.drag-handle',
      '.t-sticky-item--compact.t-sticky-item--round',
    ]
    
    // 尝试所有可能的选择器
    dragHandleSelectors.forEach(selector => {
      const dragHandle = stickyToolRef.value.$el.querySelector(selector)
      if (dragHandle) {
        dragHandle.removeEventListener('mousedown', startDrag)
        dragHandle.removeEventListener('touchstart', startTouchDrag)
      }
    })
    
    // 如果上面都没找到，尝试第一个子元素
    if (stickyToolRef.value.$el.firstElementChild) {
      stickyToolRef.value.$el.firstElementChild.removeEventListener('mousedown', startDrag)
      stickyToolRef.value.$el.firstElementChild.removeEventListener('touchstart', startTouchDrag)
    }
  }
  
  // 清理MutationObserver
  if (analysisMutationObserver) {
    analysisMutationObserver.disconnect();
    analysisMutationObserver = null;
  }
})

// 文本转换相关数据
const converterInput = ref('')
const converterOutput = ref('')

// 添加watch监听，确保输入变化时一定会触发转换
watch(converterInput, (newValue) => {
  converterOutput.value = convertTextToCSV(newValue)
}, { immediate: true })

// 处理转换器输入变化
const handleConverterInputChange = () => {
  // 实时转换文本为CSV
  converterOutput.value = convertTextToCSV(converterInput.value)
}

// 复制转换结果到剪贴板
const copyConverterOutput = async () => {
  if (!converterOutput.value) {
    NotifyPlugin.warning({
      title: '提示',
      content: '没有可复制的内容',
      placement: 'top-left',
      duration: 1000
    })
    return
  }
  
  try {
    await navigator.clipboard.writeText(converterOutput.value)
    NotifyPlugin.success({
      title: '复制成功',
      content: '已复制到剪贴板',
      placement: 'top-left',
      duration: 1000
    })
  } catch (err) {
    NotifyPlugin.error({
      title: '复制失败',
      content: '请手动复制',
      placement: 'top-left',
      duration: 1000
    })
    console.error('复制到剪贴板失败:', err)
  }
}

// 发送转换结果到后端
const sendToBackend = async () => {
  if (!converterOutput.value) {
    NotifyPlugin.warning({
      title: '提示',
      content: '没有可发送的内容',
      placement: 'top-left',
      duration: 1000
    })
    return
  }
  
  try {
    // 检查是否配置了后端URL
    if (!syncConfig.backendUrl) {
      NotifyPlugin.warning({
        title: '配置缺失',
        content: '请先在服务配置中设置后端URL',
        placement: 'top-left',
        duration: 1000
      })
      return
    }
    
    const result = await sendCSVToBackend(converterOutput.value, {
      endpoint: `${syncConfig.backendUrl}/api/questions/import`,
      headers: {
        // 可以添加额外的请求头
      }
    })
    
    NotifyPlugin.success({
      title: '发送成功',
      content: '数据已成功发送到后端',
      placement: 'top-left',
      duration: 1000
    })
    console.log('后端响应:', result)
  } catch (error) {
    NotifyPlugin.error({
      title: '发送失败',
      content: error.message,
      placement: 'top-left',
      duration: 1000
    })
    console.error('发送到后端失败:', error)
  }
}


// 添加一个辅助方法，从detail数组中提取题目信息
const extractQuestionInfo = (question) => {
  // 首先确保题目是detail格式
  const convertedQuestion = convertToDetailFormat(question);
  
  // 处理新格式
  if (convertedQuestion.detail && Array.isArray(convertedQuestion.detail)) {
    const titles = []
    const options = []
    const tables = []
    const orderedItems = [] // 按原始顺序存储题干、表格和选项
    
    let currentTitleIndex = -1 // 当前题干索引
    
    // 辅助函数：检查内容是否是以括号开头和结尾
    const isBracketWrapped = (content) => {
      return (content.startsWith('（') && content.endsWith('）')) || 
             (content.startsWith('(') && content.endsWith(')'));
    }
    
    for (let i = 0; i < convertedQuestion.detail.length; i += 2) {
      if (i + 1 >= convertedQuestion.detail.length) break
      
      const type = convertedQuestion.detail[i]
      const content = convertedQuestion.detail[i + 1]
      
      // 跳过以括号开头和结尾的内容
      if (isBracketWrapped(content)) continue;
      
      if (type === '题干') {
        titles.push(content)
        currentTitleIndex++
        orderedItems.push({ type: 'title', content, groupIndex: currentTitleIndex }) // 保存题干和组索引
      } else if (type === '表格') {
        // 解析Markdown表格为TDesign表格数据格式
        const tableData = parseMarkdownTable(content);
        tables.push(tableData);
        
        // 如果没有题干，创建一个空题干
        if (currentTitleIndex === -1) {
          titles.push('');
          currentTitleIndex++;
          orderedItems.push({ type: 'title', content: '', groupIndex: currentTitleIndex });
        }
        
        // 添加表格到有序项目列表
        orderedItems.push({
          type: 'table',
          tableData,
          groupIndex: currentTitleIndex
        });
      } else if (type === '选项') {
        // 如果没有题干，创建一个空题干
        if (currentTitleIndex === -1) {
          titles.push('')
          currentTitleIndex++
          orderedItems.push({ type: 'title', content: '', groupIndex: currentTitleIndex })
        }
        options.push({ content, groupIndex: currentTitleIndex }) // 保存选项内容和所属组
        orderedItems.push({ type: 'option', content, groupIndex: currentTitleIndex }) // 保存选项和组索引
      }
    }
    
    return {
      titles: titles,
      options: options,
      tables: tables,
      orderedItems: orderedItems // 返回按原始顺序排列的项目
    }
  }
  
  // 返回默认值
  return {
    titles: [''],
    options: [],
    tables: [],
    orderedItems: []
  }
}

// 解析Markdown表格为TDesign表格数据格式
const parseMarkdownTable = (markdownTable) => {
  const lines = markdownTable.trim().split('\n');
  
  // 确保表格至少有标题行和分隔行
  if (lines.length < 2) {
    return { data: [], columns: [] };
  }
  
  // 提取表头行
  const headerLine = lines[0];
  const headers = headerLine
    .split('|')
    .filter(cell => cell.trim() !== '')
    .map(cell => cell.trim());
  
  // 创建列定义
  const columns = headers.map((header, index) => ({
    colKey: `col${index}`,
    title: header,
    width: 'auto'
  }));
  
  // 提取数据行（跳过表头行和分隔行）
  const data = [];
  for (let i = 2; i < lines.length; i++) {
    const line = lines[i];
    const cells = line
      .split('|')
      .filter(cell => cell.trim() !== '')
      .map(cell => cell.trim());
    
    if (cells.length > 0) {
      const rowData = { id: i - 2 };
      cells.forEach((cell, cellIndex) => {
        if (cellIndex < columns.length) {
          rowData[`col${cellIndex}`] = cell;
        }
      });
      data.push(rowData);
    }
  }
  
  return { data, columns };
}

// 添加一个辅助方法，根据选项所属组和在组内的顺序生成字母标签
const getOptionLabel = (option, orderedItems) => {
  // 找出同组的所有选项
  const groupOptions = orderedItems.filter(
    item => item.type === 'option' && item.groupIndex === option.groupIndex
  );
  
  // 当前选项在同组选项中的索引位置
  const indexInGroup = groupOptions.findIndex(item => item.content === option.content);
  
  // 将索引转换为字母（A, B, C...）
  return String.fromCharCode(65 + indexInGroup);
}

// 添加辅助方法，将数组或字符串转换为可显示的文本，禁止修改formatText代码
const formatText = (text) => {
    if (typeof text === 'string') {
    return text.replace(/'/g, '"');
  } else if (Array.isArray(text)) {
    return text.join('\n').replace(/'/g, '"');
  }
  return '';
}

// 检查题目是否包含表格
const hasTable = (question) => {
  if (!question || !question.detail || !Array.isArray(question.detail)) {
    return false;
  }
  
  for (let i = 0; i < question.detail.length; i += 2) {
    if (i + 1 < question.detail.length && question.detail[i] === '表格') {
      return true;
    }
  }
  
  return false;
};

// 添加界面设置相关的状态
const uiConfig = ref({
  darkMode: localStorage.getItem('theme-mode') === 'dark',
  fontSize: parseInt(localStorage.getItem('font-size') || '0', 10), // 默认为0
  textOpacity: parseInt(localStorage.getItem('text-opacity') || '30', 10) // 默认透明度30%
})

// 确保在DOM加载完毕后应用字体大小设置
const ensureFontSizeApplied = () => {
  // 检查内容区域是否已经存在
  const contentArea = document.querySelector('.content-area')
  if (contentArea) {
    // 内容区域存在，直接应用字体大小
    applyFontSize(uiConfig.value.fontSize)
    
    // 如果是暗黑模式，同时应用文字透明度
    if (uiConfig.value.darkMode) {
      applyTextOpacity(uiConfig.value.textOpacity)
    }
  } else {
    // 内容区域不存在，等待一段时间后再试
    setTimeout(() => {
      ensureFontSizeApplied()
    }, 100)
  }
}

// 计算属性：明亮模式状态
const lightMode = computed({
  get: () => !uiConfig.value.darkMode,
  set: (val) => {
    uiConfig.value.darkMode = !val;
    handleDarkModeChange();
  }
})

// 处理暗黑模式变更
const handleDarkModeChange = () => {
  // 处理暗黑模式
  if (uiConfig.value.darkMode) {
    document.documentElement.setAttribute('theme-mode', 'dark')
    // 更新状态栏颜色为深色
    document.querySelector('meta[name="theme-color"][media="(prefers-color-scheme: dark)"]').setAttribute('content', '#242424')
    document.querySelector('meta[name="theme-color"][media="(prefers-color-scheme: light)"]').setAttribute('content', '#242424')
    
    // 在切换到暗黑模式时应用文字透明度
    applyTextOpacity(uiConfig.value.textOpacity)
  } else {
    document.documentElement.removeAttribute('theme-mode')
    // 更新状态栏颜色为浅色
    document.querySelector('meta[name="theme-color"][media="(prefers-color-scheme: dark)"]').setAttribute('content', '#ffffff')
    document.querySelector('meta[name="theme-color"][media="(prefers-color-scheme: light)"]').setAttribute('content', '#ffffff')
    
    // 切换到明亮模式时重置文字不透明度
    const contentArea = document.querySelector('.content-area')
    if (contentArea) {
      const textElements = contentArea.querySelectorAll('h3, .option-item, .simple-analysis, .detailed-analysis')
      textElements.forEach(el => {
        el.style.opacity = '1'
      })
    }
  }
  localStorage.setItem('theme-mode', uiConfig.value.darkMode ? 'dark' : 'light')
  
  // 检测是否是Safari浏览器
  const isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent)
  
  // 仅在Safari浏览器中重新加载页面
  if (isSafari) {
    window.location.reload()
  }
}

// 处理字体大小变更
const handleFontSizeChange = () => {
  // 处理字体大小
  applyFontSize(uiConfig.value.fontSize)
  localStorage.setItem('font-size', uiConfig.value.fontSize.toString())
}

// 处理文字透明度变更
const handleTextOpacityChange = () => {
  // 保存到本地存储
  localStorage.setItem('text-opacity', uiConfig.value.textOpacity.toString())
  // 应用文字透明度
  applyTextOpacity(uiConfig.value.textOpacity)
  
  // 立即应用到所有已存在的解析框
  if (uiConfig.value.darkMode) {
    const contentArea = document.querySelector('.content-area')
    if (contentArea) {
      const opacityValue = 1 - (uiConfig.value.textOpacity / 100)
      const analysisTexts = contentArea.querySelectorAll('.analysis .simple-analysis, .analysis .detailed-analysis')
      analysisTexts.forEach(el => {
        el.style.opacity = opacityValue.toString()
      })
    }
  }
}

// 应用文字透明度
const applyTextOpacity = (opacity) => {
  if (!uiConfig.value.darkMode) return // 只在暗黑模式下应用

  // 获取内容区域
  const contentArea = document.querySelector('.content-area')
  if (contentArea) {
    // 计算不透明度值（1 - 透明度/100）
    const opacityValue = 1 - (opacity / 100)
    
    // 设置内容区的文字不透明度
    contentArea.style.setProperty('--text-opacity', opacityValue.toString())
    
    // 应用不透明度到主要文本元素
    const textElements = contentArea.querySelectorAll('h3, .option-item, .simple-analysis, .detailed-analysis')
    textElements.forEach(el => {
      el.style.opacity = opacityValue.toString()
    })
  }
}

// 应用字体大小调整
const applyFontSize = (sizeChange) => {
  // 只对主界面的内容区域应用字体大小调整
  const contentArea = document.querySelector('.content-area')
  if (contentArea) {
    // 基准字体大小 + 调整值
    const newSize = 18 + sizeChange // 基础大小为18px
    const marginScale = 1 + (sizeChange / 8) // margin比例，随字体大小变化而更明显变化，增大缩放幅度
    
    // 设置内容区的基础字体大小
    contentArea.style.fontSize = `${newSize}px`
    
    // 更新内容区的h3标题
    const h3Elements = contentArea.querySelectorAll('h3')
    h3Elements.forEach(el => {
      el.style.fontSize = `${newSize + 4}px` // h3比正文大4px
      
      if (sizeChange === 0) {
        // 恢复初始状态
        el.style.marginBottom = '11px'
        el.style.marginTop = '8px'
      } else {
        // 调整margin
        el.style.marginBottom = `${11 * marginScale}px`
        el.style.marginTop = `${8 * marginScale}px`
      }
    })
    
    // 更新选项的字体大小和边距
    const optionItems = contentArea.querySelectorAll('.option-item')
    optionItems.forEach(el => {
      el.style.fontSize = `${newSize}px`
      
      if (sizeChange === 0) {
        // 恢复初始状态
        el.style.marginBottom = '6px'
      } else {
        // 调整margin
        el.style.marginBottom = `${6 * marginScale}px`
      }
    })
    
    // 更新解析文本的字体大小和边距
    const analysisTexts = contentArea.querySelectorAll('.simple-analysis, .detailed-analysis')
    analysisTexts.forEach(el => {
      el.style.fontSize = `${newSize - 2}px` // 比正文小2px
      
      if (sizeChange === 0) {
        // 恢复初始状态
        el.style.marginBottom = '4px'
      } else {
        // 调整margin
        el.style.marginBottom = `${4 * marginScale}px`
      }
    })
    
    // 更新分析区块的margin
    const analysisBlocks = contentArea.querySelectorAll('.analysis')
    analysisBlocks.forEach(el => {
      if (sizeChange === 0) {
        // 恢复初始状态
        el.style.marginTop = '20px'
      } else {
        // 调整margin
        el.style.marginTop = `${20 * marginScale}px`
      }
    })
    
    // 更新星级评分区域的margin
    const starBlocks = contentArea.querySelectorAll('.question-stars')
    starBlocks.forEach(el => {
      if (sizeChange === 0) {
        // 恢复初始状态
        el.style.marginTop = '20px'
        el.style.marginBottom = '4px'
      } else {
        // 调整margin
        el.style.marginTop = `${20 * marginScale}px`
        el.style.marginBottom = `${4 * marginScale}px`
      }
    })
  }
}

// 字体大小调整的刻度
const fontSizeMarks = {
  '-4': '-4',
  '-2': '-2',
  '0': '0',
  '2': '+2',
  '4': '+4',
  '6': '+6'
}

// 文字透明度的刻度
const textOpacityMarks = {
  '0': '0%',
  '30': '30%',
  '70': '70%'
}

// 添加监听uiConfig中暗黑模式的变化
watch(
  () => uiConfig.value.darkMode,
  (newValue) => {
    if (newValue) {
      // 切换到暗黑模式时应用文字透明度
      applyTextOpacity(uiConfig.value.textOpacity)
    } else {
      // 切换到明亮模式时重置文字不透明度
      const contentArea = document.querySelector('.content-area')
      if (contentArea) {
        const textElements = contentArea.querySelectorAll('h3, .option-item, .simple-analysis, .detailed-analysis')
        textElements.forEach(el => {
          el.style.opacity = '1'
        })
      }
    }
  }
)

// 打开编辑题目对话框
const openEditDialog = (question) => {
  editingQuestion.value = question
  
  // 获取完整的题目记录并转换为JSON字符串
  const db = initDB()
  db.then(async (database) => {
    // 使用getPaperStoreName获取正确的存储区名称
    const paperStoreName = getPaperStoreName(currentPaper.value.id)
    const fullQuestion = await database.get(paperStoreName, question.id)
    if (fullQuestion) {
      // 创建一个副本，去除不必要的UI状态属性
      const questionCopy = { ...fullQuestion }
      delete questionCopy.showAnalysis  // 删除UI状态属性
      
      // 格式化JSON，使其更易读
      editingQuestionContent.value = JSON.stringify(questionCopy, null, 2)
      editQuestionDialogVisible.value = true
    }
  }).catch(error => {
    console.error('获取题目数据失败:', error)
    NotifyPlugin.error({
      title: '获取失败',
      content: '获取题目数据失败，star数据不要存储到config里，要存储到indexeddb中的试卷数据表里',
      placement: 'top-left',
      duration: 3000
    })
  })
}

// 确认编辑题目
const confirmEditQuestion = async () => {
  // 获取新的题目内容
  const newContent = editingQuestionContent.value.trim()
  
  try {
    // 如果内容为空，显示确认删除对话框
    if (!newContent) {
      deleteQuestionDialogVisible.value = true
      return
    }
    
    // 直接解析JSON而不是使用parseQuestion函数
    let parsedQuestion
    try {
      parsedQuestion = JSON.parse(newContent)
    } catch (jsonError) {
      throw new Error('题目JSON格式错误: ' + jsonError.message)
    }
    
    if (!parsedQuestion || !parsedQuestion.detail || parsedQuestion.detail.length === 0) {
      throw new Error('题目格式不正确或缺少必要的detail字段')
    }
    
    // 更新题目数据
    const db = await initDB()
    const paperStoreName = getPaperStoreName(currentPaper.value.id)
    
    if (!db.objectStoreNames.contains(paperStoreName)) {
      throw new Error('试卷存储区不存在')
    }
    
    // 获取题目完整数据
    const existingQuestion = await db.get(paperStoreName, editingQuestion.value.id)
    
    if (!existingQuestion) {
      throw new Error('题目不存在')
    }
    
    // 更新题目内容，保留其他字段
    existingQuestion.detail = parsedQuestion.detail
    if (parsedQuestion.answer) existingQuestion.answer = parsedQuestion.answer
    if (parsedQuestion.simpleAnswer) existingQuestion.simpleAnswer = parsedQuestion.simpleAnswer
    if (parsedQuestion.keywords) existingQuestion.keywords = parsedQuestion.keywords
    if (parsedQuestion.stars !== undefined) existingQuestion.stars = parsedQuestion.stars

    await db.put(paperStoreName, existingQuestion)
    
    // 更新当前题目显示
    const index = currentQuestions.value.findIndex(q => q.id === editingQuestion.value.id)
    if (index !== -1) {
      Object.assign(currentQuestions.value[index], existingQuestion)
    }
    
    // 更新试卷的最后修改时间
    await updatePaperLastModifiedTime(currentPaper.value.id)
    
    // 关闭编辑对话框
    editQuestionDialogVisible.value = false
    editingQuestion.value = null
    editingQuestionContent.value = ''
    
    // 显示成功通知
    NotifyPlugin.success({
      title: '编辑成功',
      content: '题目已成功更新',
      placement: 'top-left',
      duration: 1000
    })
    
  } catch (error) {
    console.error('编辑题目失败:', error)
    NotifyPlugin.error({
      title: '编辑失败',
      content: error.message,
      placement: 'top-left',
      duration: 3000
    })
  }
}

// 取消编辑题目
const cancelEditQuestion = () => {
  editQuestionDialogVisible.value = false
  deleteQuestionDialogVisible.value = false
  editingQuestion.value = null
  editingQuestionContent.value = ''
}

// 确认删除题目
const confirmDeleteQuestion = async () => {
  try {
    // 获取题目ID
    const questionId = editingQuestion.value.id
    
    // 从数据库删除题目
    const db = await initDB()
    const paperStoreName = getPaperStoreName(currentPaper.value.id)
    
    if (!db.objectStoreNames.contains(paperStoreName)) {
      throw new Error('试卷存储区不存在')
    }
    
    await db.delete(paperStoreName, questionId)
    
    // 从当前题目列表中删除
    const index = currentQuestions.value.findIndex(q => q.id === questionId)
    if (index !== -1) {
      currentQuestions.value.splice(index, 1)
    }
    
    // 关闭对话框
    deleteQuestionDialogVisible.value = false
    editQuestionDialogVisible.value = false
    editingQuestion.value = null
    editingQuestionContent.value = ''
    
    // 显示成功通知
    NotifyPlugin.success({
      title: '删除成功',
      content: '题目已成功删除',
      placement: 'top-left',
      duration: 1000
    })
    
    // 删除试题需要记录更新时间并安排同步
    await updatePaperLastModifiedTime(currentPaper.value.id)
    // 安排延迟同步
    scheduleDelayedSync()

  } catch (error) {
    console.error('删除题目失败:', error)
    NotifyPlugin.error({
      title: '删除失败',
      content: error.message,
      placement: 'top-left',
      duration: 3000
    })
  }
}

// 取消删除题目
const cancelDeleteQuestion = () => {
  deleteQuestionDialogVisible.value = false
}

// 实现S3备份功能
const backupToS3 = async () => {
  try {
    console.log('开始备份数据到S3...');
    
    // 确保endpoint干净，不包含存储桶名称
    syncConfig.value.s3Endpoint = cleanEndpointUrl(syncConfig.value.s3Endpoint);
    
    // 打印调试信息
    console.log('S3配置信息:', {
      endpoint: syncConfig.value.s3Endpoint,
      region: syncConfig.value.s3Region,
      bucket: syncConfig.value.s3Bucket
    });
    
    // 初始化S3客户端
    const s3Client = new S3Client({
      region: syncConfig.value.s3Region,  // 使用用户配置的 region
      endpoint: syncConfig.value.s3Endpoint,
      credentials: {
        accessKeyId: syncConfig.value.s3AccessKeyId,
        secretAccessKey: syncConfig.value.s3SecretAccessKey
      },
      forcePathStyle: true, // 强制使用路径样式URL，避免将存储桶名称作为子域名
      // 添加CORS相关配置
      customUserAgent: 'TDesign-Backup-App/1.0'
    });
    
    // 从数据库获取所有数据
    const db = await initDB();
    
    // 获取试卷数据
    const papersMap = await safeGetConfig('papers', {});
    const allPapers = [];
    
    // 将试卷映射转换为数组
    for (const paperId in papersMap) {
      allPapers.push({
        id: paperId,
        name: papersMap[paperId]
      });
    }
    
    // 创建时间戳，用于所有备份文件名
    const timestamp = Date.now();
    const date = new Date();
    const dateStr = date.toISOString().split('T')[0]; // 格式如 "2023-07-21"
    
    // 上传成功的文件列表
    const uploadedFiles = [];
    
    // 1. 备份config数据
    try {
      // 获取config数据
      const serviceConfigData = await safeGetConfig('service_config', defaultConfig);
      // 获取试卷更新时间数据
      const paperUpdateTimes = await safeGetConfig('papers_update_time', {});
      
      // 构建config备份数据
      const configBackup = {
        timestamp: timestamp,
        service_config: serviceConfigData,
        papers: papersMap,  // 保存试卷映射
        papers_update_time: paperUpdateTimes  // 保存试卷更新时间
      };
      
      // 创建config文件名
      const configFileName = `config.json`;
      
      // 上传config数据
      const configUploadParams = {
        Bucket: syncConfig.value.s3Bucket,
        Key: configFileName,
        Body: JSON.stringify(configBackup),
        ContentType: 'application/json'
      };
      
      const configCommand = new PutObjectCommand(configUploadParams);
      await s3Client.send(configCommand);
      console.log('配置数据备份成功:', configFileName);
      uploadedFiles.push(configFileName);
    } catch (configError) {
      console.error('配置数据备份失败:', configError);
      throw configError;
    }
    
    // 2. 分别备份每个试卷数据
    for (const paper of allPapers) {
      try {
        const paperStoreName = getPaperStoreName(paper.id);
        
        // 检查存储区是否存在
        if (db.objectStoreNames.contains(paperStoreName)) {
          // 获取试卷中的所有题目
          const tx = db.transaction(paperStoreName, 'readonly');
          const questions = await tx.store.getAll();
          await tx.done;
          
          // 构建试卷备份数据
          const paperBackup = {
            timestamp: timestamp,
            paper: {
              id: paper.id,
              name: paper.name
            },
            questions: questions
          };
          
          // 创建试卷文件名
          const paperFileName = `paper_${paper.id}.json`;
          
          // 上传试卷数据
          const paperUploadParams = {
            Bucket: syncConfig.value.s3Bucket,
            Key: paperFileName,
            Body: JSON.stringify(paperBackup),
            ContentType: 'application/json'
          };
          
          const paperCommand = new PutObjectCommand(paperUploadParams);
          await s3Client.send(paperCommand);
          console.log(`试卷 "${paper.name}" 备份成功:`, paperFileName);
          uploadedFiles.push(paperFileName);
        }
      } catch (paperError) {
        console.error(`试卷 "${paper.name}" 备份失败:`, paperError);
        // 继续备份其他试卷，不中断整个过程
      }
    }
    
    // 清理旧的备份文件，保留每种类型的最新备份
    try {
      await cleanupOldBackups(s3Client);
    } catch (cleanupError) {
      console.error('清理旧备份失败 (可能是CORS问题):', cleanupError);
      // 这里不抛出错误，因为主要备份已成功
    }
    
    // 显示成功消息
    NotifyPlugin.success({
      title: '备份成功',
      content: `已备份${uploadedFiles.length}个文件到S3`,
      placement: 'top-left',
      duration: 3000
    });
    
    return true;
  } catch (error) {
    console.error('备份到S3失败:', error);
    
    // 显示错误消息
    NotifyPlugin.error({
      title: '备份失败',
      content: error.message,
      placement: 'top-left',
      duration: 3000
    });
    
    return false;
  }
};

// 清理旧的备份文件，只保留每种类型的最新备份
const cleanupOldBackups = async (s3Client) => {
  try {
    // 列出存储桶中的所有对象
    const listCommand = new ListObjectsV2Command({
      Bucket: syncConfig.value.s3Bucket
    });
    
    const response = await s3Client.send(listCommand);
    
    if (!response.Contents || response.Contents.length === 0) {
      return;
    }
    
    // 按文件类型分组
    const fileGroups = {};
    
    response.Contents.forEach(file => {
      // 根据文件名确定类型
      let fileType;
      if (file.Key === 'config.json') {
        fileType = 'config';
      } else if (file.Key.startsWith('paper_') && file.Key.endsWith('.json')) {
        // 从文件名提取试卷ID
        const paperId = file.Key.replace('paper_', '').replace('.json', '');
        fileType = `paper_${paperId}`;
      } else if (file.Key.startsWith('backup-')) {
        // 旧格式的备份文件
        fileType = 'old_backup';
      } else {
        // 其他文件
        fileType = 'other';
      }
      
      if (!fileGroups[fileType]) {
        fileGroups[fileType] = [];
      }
      fileGroups[fileType].push(file);
    });
    
    // 对每种文件类型，只保留最新的一个文件
    const filesToDelete = [];
    
    for (const fileType in fileGroups) {
      const files = fileGroups[fileType];
      
      if (files.length > 1) {
        // 按最后修改时间排序
        files.sort((a, b) => b.LastModified.getTime() - a.LastModified.getTime());
        
        // 获取要删除的文件（只保留最新的1个）
        const toDelete = files.slice(1);
        filesToDelete.push(...toDelete);
      }
    }
    
    console.log(`将删除 ${filesToDelete.length} 个旧备份文件`);
    
    // 删除旧文件
    for (const file of filesToDelete) {
      const deleteCommand = new DeleteObjectCommand({
        Bucket: syncConfig.value.s3Bucket,
        Key: file.Key
      });
      
      await s3Client.send(deleteCommand);
      console.log(`已删除旧备份: ${file.Key}`);
    }
  } catch (error) {
    console.error('清理旧备份文件失败:', error);
    throw error; // 重新抛出错误以便上层捕获
  }
};

// 添加同步功能相关状态
const backupLoading = ref(false);
const restoreDialogVisible = ref(false);
const backupsList = ref([]);
const selectedBackup = ref(null);
const restoreLoading = ref(false);
const syncLoading = ref(false);

// 显示恢复对话框 - 不再展示对话框，直接执行恢复操作
const showRestoreDialog = async () => {
  try {
    // 检查S3配置是否完整
    if (!syncConfig.value.s3Endpoint || !syncConfig.value.s3Region || 
        !syncConfig.value.s3AccessKeyId || !syncConfig.value.s3SecretAccessKey || 
        !syncConfig.value.s3Bucket) {
      NotifyPlugin.warning({
        title: '配置不完整',
        content: '请先完成S3存储的配置',
        placement: 'top-left',
        duration: 3000
      });
      return;
    }
    
  // 确保endpoint干净，不包含存储桶名称
    syncConfig.value.s3Endpoint = cleanEndpointUrl(syncConfig.value.s3Endpoint);
    
    // 初始化S3客户端
    const s3Client = new S3Client({
      region: syncConfig.value.s3Region,
      endpoint: syncConfig.value.s3Endpoint,
      credentials: {
        accessKeyId: syncConfig.value.s3AccessKeyId,
        secretAccessKey: syncConfig.value.s3SecretAccessKey
      },
      forcePathStyle: true
    });
    
    // 首先下载config.json以比较试卷更新时间
    console.log('下载config.json以比较试卷更新时间');
    
    try {
      // 获取config.json
      const getConfigCommand = new GetObjectCommand({
        Bucket: syncConfig.value.s3Bucket,
        Key: 'config.json'
      });
      
      const configResponse = await s3Client.send(getConfigCommand);
      
      // 读取响应流 - 使用SDK提供的方法处理
      const configBuffer = await configResponse.Body.transformToString();
      
      // 解析config数据
      cloudConfig = JSON.parse(configBuffer);
      console.log('成功从S3下载config.json');
      
      // 处理service_config配置的合并
      const localServiceConfig = await safeGetConfig('service_config', defaultConfig);
      
      // 确保cloudConfig中有service_config对象
      if (!cloudConfig.service_config) {
        cloudConfig.service_config = {};
      }
      
      // 对于service_config的token和model键值：
      // 如果本地非空而云端为空，则使用本地值
      // 如果云端非空，则保留云端值
      if (localServiceConfig.token && !cloudConfig.service_config.token) {
        console.log('本地token非空而云端为空，使用本地值');
        cloudConfig.service_config.token = localServiceConfig.token;
      }
      
      if (localServiceConfig.model && !cloudConfig.service_config.model) {
        console.log('本地model非空而云端为空，使用本地值');
        cloudConfig.service_config.model = localServiceConfig.model;
      }
      
      // 更新本地UI界面的值
      serviceConfig.value = { ...cloudConfig.service_config };
      
      // 保存到本地IndexedDB
      await safePutConfig('service_config', serviceConfig.value);
      // 同时更新localStorage（兼容旧版本）
      localStorage.setItem(STORAGE_KEY, JSON.stringify(serviceConfig.value));
      console.log('已合并本地和云端AI服务配置');
      
      // 获取本地试卷更新时间
      const localPaperUpdateTimes = await safeGetConfig('papers_update_time', {});
      
      // 获取云端试卷更新时间 (可能在不同位置，根据备份格式确定)
      let cloudPaperUpdateTimes = {};
      if (cloudConfig.papers_update_time) {
        cloudPaperUpdateTimes = cloudConfig.papers_update_time;
      }
      
      console.log('本地试卷更新时间:', localPaperUpdateTimes);
      console.log('云端试卷更新时间:', cloudPaperUpdateTimes);
      
      // 比较所有试卷的更新时间，确定需要下载的试卷
      const papersToDownload = [];
      
      // 合并所有试卷ID（本地和云端）
      const allPaperIds = [...new Set([
        ...Object.keys(localPaperUpdateTimes),
        ...Object.keys(cloudPaperUpdateTimes)
      ])];
      
      for (const paperId of allPaperIds) {
        const localTime = localPaperUpdateTimes[paperId] || 0; // 默认为0
        const cloudTime = cloudPaperUpdateTimes[paperId] || 0; // 默认为0
        
        // 如果云端版本更新，则下载
        if (cloudTime > localTime) {
          papersToDownload.push(paperId);
          console.log(`试卷 ${paperId} 云端更新时间更新，需要下载`);
        } else {
          console.log(`试卷 ${paperId} 本地版本已是最新，不需下载`);
        }
      }
      
      if (papersToDownload.length === 0) {
        NotifyPlugin.info({
          title: '无需恢复',
          content: '本地数据已是最新，无需恢复',
          placement: 'top-left',
          duration: 2000
        });
        return;
      }
      
      // 显示正在恢复的提示
      restoreLoading.value = true;
      
      // 下载需要更新的试卷JSON
      for (const paperId of papersToDownload) {
        try {
          const paperFileName = `paper_${paperId}.json`;
          
          // 获取试卷数据
          const getPaperCommand = new GetObjectCommand({
            Bucket: syncConfig.value.s3Bucket,
            Key: paperFileName
          });
          
          const paperResponse = await s3Client.send(getPaperCommand);
          
          // 读取响应流 - 使用SDK提供的方法处理
          const paperBuffer = await paperResponse.Body.transformToString();
          
          // 解析试卷数据
          const paperData = JSON.parse(paperBuffer);
          
          // 更新本地数据库
          await updatePaperFromBackup(paperId, paperData);
          
          console.log(`试卷 ${paperId} 恢复成功`);
        } catch (paperError) {
          console.error(`试卷 ${paperId} 恢复失败:`, paperError);
        }
      }
      
      // 更新本地试卷更新时间
      for (const paperId of papersToDownload) {
        localPaperUpdateTimes[paperId] = cloudPaperUpdateTimes[paperId] || Date.now();
      }
      
      // 保存更新后的时间戳
      await safePutConfig('papers_update_time', localPaperUpdateTimes);
      
      // 刷新UI
      if (currentPaper.value) {
        currentQuestions.value = await loadQuestions(currentPaper.value.id);
      }
      
      NotifyPlugin.success({
        title: '恢复成功',
        content: `已恢复 ${papersToDownload.length} 个试卷的数据`,
        placement: 'top-left',
        duration: 3000
      });
      
    } catch (configError) {
      console.error('获取config.json失败:', configError);
      NotifyPlugin.error({
        title: '恢复失败',
        content: '无法获取云端配置数据',
        placement: 'top-left',
        duration: 3000
      });
    } finally {
      restoreLoading.value = false;
    }
  } catch (error) {
    console.error('恢复过程失败:', error);
    NotifyPlugin.error({
      title: '恢复失败',
      content: error.message,
      placement: 'top-left',
      duration: 3000
    });
    restoreLoading.value = false;
  }
};

// 格式化文件大小
const formatFileSize = (bytes) => {
  if (bytes < 1024) return bytes + ' B';
  if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(2) + ' KB';
  return (bytes / (1024 * 1024)).toFixed(2) + ' MB';
};

// 恢复所选备份 - 现在直接调用showRestoreDialog函数执行恢复逻辑
const handleRestoreBackup = async () => {
  // 这个函数现在仅作为对话框的确认处理函数存在
  // 恢复逻辑已经移到showRestoreDialog中
  // 关闭对话框
  restoreDialogVisible.value = false;
};

// 取消恢复操作
const cancelRestore = () => {
  restoreDialogVisible.value = false;
  selectedBackup.value = null;
};

// 选择备份
const selectBackup = (backup) => {
  // 先取消所有备份的选中状态
  backupsList.value.forEach(item => {
    item.selected = false;
  });
  
  // 设置当前备份的选中状态
  backup.selected = true;
  
  // 保存所选备份的key
  selectedBackup.value = backup.key;
};

// 从S3恢复数据
const restoreFromS3 = async (backupKey) => {
  try {
    console.log(`开始从S3恢复数据: ${backupKey}...`);
    
    // 确保endpoint干净，不包含存储桶名称
    syncConfig.value.s3Endpoint = cleanEndpointUrl(syncConfig.value.s3Endpoint);
    
    // 初始化S3客户端
    const s3Client = new S3Client({
      region: syncConfig.value.s3Region,  // 使用用户配置的 region
      endpoint: syncConfig.value.s3Endpoint,
      credentials: {
        accessKeyId: syncConfig.value.s3AccessKeyId,
        secretAccessKey: syncConfig.value.s3SecretAccessKey
      },
      forcePathStyle: true // 强制使用路径样式URL
    });
    
    // 从S3获取备份文件
    const getCommand = new GetObjectCommand({
      Bucket: syncConfig.value.s3Bucket,
      Key: backupKey
    });
    
    const response = await s3Client.send(getCommand);
    
    // 读取响应流 - 使用SDK提供的方法处理
    const responseBuffer = await response.Body.transformToString();
    
    // 解析备份数据
    const backupData = JSON.parse(responseBuffer);
    
    // 检查备份数据完整性
    if (!backupData.papers) {
      throw new Error('备份数据不完整或格式错误');
    }
    
    // 确认恢复操作
    const confirmRestore = confirm('此操作将用备份数据替换当前所有数据，确定要继续吗？');
    
    if (!confirmRestore) {
      console.log('用户取消了恢复操作');
      return false;
    }
    
    // 执行恢复操作 - 清空并重建所有数据
    await rebuildLocalDataFromBackup(backupData);
    
    // 显示成功消息
    NotifyPlugin.success({
      title: '恢复成功',
      content: '已从S3备份恢复数据',
      placement: 'top-left',
      duration: 3000
    });
    
    return true;
  } catch (error) {
    console.error('从S3恢复数据失败:', error);
    
    // 显示错误消息
    NotifyPlugin.error({
      title: '恢复失败',
      content: error.message,
      placement: 'top-left',
      duration: 3000
    });
    
    return false;
  }
};

// 从备份数据重建本地数据库
const rebuildLocalDataFromBackup = async (backupData) => {
  try {
    console.log('开始从备份数据重建本地数据库...');
    let db = await initDB();
    
    // 1. 先保存所有需要创建的表名
    const paperIds = Object.keys(backupData.paperQuestions || {});
    
    // 2. 首先关闭数据库连接
    db.close();
    
    // 3. 使用openDB直接删除旧数据库并创建新的数据库
    const { openDB } = await import('idb');
    db = await openDB(dbName, 1, {
      upgrade(db) {
        // 创建基础表
        if (!db.objectStoreNames.contains(papersStoreName)) {
          db.createObjectStore(papersStoreName, { keyPath: 'id' });
        }
        if (!db.objectStoreNames.contains(configStoreName)) {
          db.createObjectStore(configStoreName, { keyPath: 'id' });
        }
        
        // 创建所有试卷表
        for (const paperId of paperIds) {
          if (!db.objectStoreNames.contains(paperId)) {
            db.createObjectStore(paperId, { keyPath: 'id' });
            console.log(`已创建试卷表: ${paperId}`);
          }
        }
      }
    });
    
    // 4. 保存所有试卷数据
    const tx = db.transaction(papersStoreName, 'readwrite');
    for (const paper of backupData.papers) {
      await tx.store.put(paper);
    }
    await tx.done;
    console.log(`已恢复 ${backupData.papers.length} 个试卷信息`);
    
    // 5. 保存所有题目数据
    for (const [paperId, questions] of Object.entries(backupData.paperQuestions)) {
      try {
        if (db.objectStoreNames.contains(paperId)) {
          const tx = db.transaction(paperId, 'readwrite');
          for (const question of questions) {
            await tx.store.put(question);
          }
          await tx.done;
          console.log(`已恢复试卷 ${paperId} 的 ${questions.length} 个题目`);
        } else {
          console.warn(`跳过不存在的试卷表: ${paperId}`);
        }
      } catch (error) {
        console.error(`恢复试卷 ${paperId} 的题目时出错:`, error);
      }
    }
    
    // 6. 恢复配置数据
    const configTx = db.transaction(configStoreName, 'readwrite');
    if (backupData.config.paper_timestamps) {
      await configTx.store.put({
        id: 'paper_timestamps',
        data: backupData.config.paper_timestamps
      });
      console.log('已恢复试卷时间戳配置');
    }
    
    if (backupData.config.service_config) {
      await configTx.store.put({
        id: 'service_config',
        data: backupData.config.service_config
      });
      console.log('已恢复API服务配置');
    }
    
    if (backupData.config.font_size) {
      await configTx.store.put({
        id: 'font_size',
        data: backupData.config.font_size
      });
      console.log('已恢复字体大小配置');
    }
    await configTx.done;
    
    // 7. 更新UI（重新加载数据）
    papers.value = await db.getAll(papersStoreName);
    if (currentPaper.value) {
      currentQuestions.value = await loadQuestions(currentPaper.value.id);
    }
    
    console.log('数据库恢复完成');
    return true;
  } catch (error) {
    console.error('从备份恢复数据库失败:', error);
    throw error; // 重新抛出错误以便上层捕获
  }
};

// 手动备份功能
const manualBackup = async () => {
  try {
    backupLoading.value = true;
    const result = await backupToS3();
    if (result) {
      NotifyPlugin.success({
        title: '备份成功',
        content: '已成功备份到S3存储',
        placement: 'top-left',
        duration: 2000
      });
    }
  } catch (error) {
    console.error('手动备份失败:', error);
    NotifyPlugin.error({
      title: '备份失败',
      content: error.message,
      placement: 'top-left',
      duration: 3000
    });
  } finally {
    backupLoading.value = false;
  }
};

// 更新试卷最后修改时间的函数
const updatePaperLastModifiedTime = async (paperId) => {
  try {
    if (!paperId) {
      console.warn('未指定试卷ID，无法更新修改时间')
      return false
    }

    // 从config表中获取papers_update_time映射数据，如果不存在则创建空对象
    let updateTimesMap = await safeGetConfig('papers_update_time', {})
    
    // 更新当前试卷的最后修改时间
    updateTimesMap[paperId] = Date.now()
    
    // 保存回config表
    await safePutConfig('papers_update_time', updateTimesMap)
    
    console.log(`已更新试卷 ${paperId} 的最后修改时间`)
    return true
  } catch (error) {
    console.error('更新试卷修改时间失败:', error)
    return false
  }
}

// 获取试卷的最后修改时间
const getPaperLastModifiedTime = async (paperId) => {
  try {
    if (!paperId) {
      return null
    }
    
    // 从config表中获取papers_update_time映射数据
    const updateTimesMap = await safeGetConfig('papers_update_time', {})
    
    // 返回指定试卷的最后修改时间，如果不存在则返回null
    return updateTimesMap[paperId] || null
  } catch (error) {
    console.error('获取试卷修改时间失败:', error)
    return null
  }
}

// 获取所有试卷的最后修改时间
const getAllPapersLastModifiedTimes = async () => {
  try {
    // 从config表中获取papers_update_time映射数据
    const updateTimesMap = await safeGetConfig('papers_update_time', {})
    return updateTimesMap || {}
  } catch (error) {
    console.error('获取所有试卷修改时间失败:', error)
    return {}
  }
}

// 从备份数据更新单个试卷
const updatePaperFromBackup = async (paperId, paperData) => {
  try {
    console.log(`开始更新试卷 ${paperId} 的数据...`);
    
    // 获取试卷数据
    let paper = paperData.paper;
    let questions = paperData.questions;
    
    if (!paper || !questions) {
      throw new Error('备份数据格式不正确');
    }
    
    // 获取试卷存储区名称
    const paperStoreName = getPaperStoreName(paperId);
    
    // 更新 papers 映射中的试卷名称
    const papersMap = await safeGetConfig('papers', {});
    papersMap[paperId] = paper.name;
    await safePutConfig('papers', papersMap);
    
    // 连接数据库
    const db = await initDB();
    
    // 检查试卷存储区是否存在，不存在则创建
    if (!db.objectStoreNames.contains(paperStoreName)) {
      console.log(`试卷存储区 ${paperStoreName} 不存在，创建新的存储区`);
      
      // 关闭数据库连接
      db.close();
      
      // 使用新版本创建存储区
      const newVersion = currentDBVersion + 1;
      const newDb = await openDB(dbName, newVersion, {
        upgrade(db, oldVersion, newVersion, transaction) {
          // 创建试卷存储区
          db.createObjectStore(paperStoreName, { keyPath: 'id' });
          console.log(`已创建试卷存储区 ${paperStoreName}`);
        }
      });
      
      // 更新当前版本号
      currentDBVersion = newVersion;
      localStorage.setItem('dbVersion', currentDBVersion.toString());
      
      // 导入所有题目
      const tx = newDb.transaction(paperStoreName, 'readwrite');
      for (const question of questions) {
        await tx.store.put(question);
      }
      await tx.done;
      
      // 关闭数据库连接
      newDb.close();
    } else {
      // 如果存储区已存在，更新数据
      console.log(`试卷存储区 ${paperStoreName} 已存在，更新数据`);
      
      // 删除现有题目并导入新题目
      const tx = db.transaction(paperStoreName, 'readwrite');
      // 先获取所有现有题目ID
      const existingQuestions = await tx.store.getAll();
      
      // 删除现有题目
      for (const existingQuestion of existingQuestions) {
        await tx.store.delete(existingQuestion.id);
      }
      
      // 导入新题目
      for (const question of questions) {
        await tx.store.put(question);
      }
      await tx.done;
      
      // 关闭数据库连接
      db.close();
    }
    
    // 更新UI时，如果是当前试卷，刷新题目列表
    if (currentPaper.value && currentPaper.value.id === paperId) {
      // 更新试卷名称
      currentPaper.value.name = paper.name;
      // 刷新题目列表
      currentQuestions.value = await loadQuestions(paperId);
    }
    
    // 更新papers列表中的试卷名称
    const paperIndex = papers.value.findIndex(p => p.id === paperId);
    if (paperIndex !== -1) {
      papers.value[paperIndex].name = paper.name;
    } else {
      // 如果试卷不在列表中，添加到列表
      papers.value.push({
        id: paperId,
        name: paper.name
      });
    }
    
    return true;
  } catch (error) {
    console.error(`更新试卷 ${paperId} 失败:`, error);
    throw error;
  }
};

</script>

<style scoped>


p{
    line-height: 1.5;
}

.app-container {
  display: flex;
  flex-direction: column;
  height: 100vh;
  background-color: white;
  color: black;
}

.content-area {
  flex: 1;
  overflow: hidden;
}

.content-list-container {
  height: 100%;
}

.question-content {
  padding: 16px;
  cursor: text;
}

.question-header {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  margin-bottom: 16px;
}

.question-number {
  font-weight: 500;
  color: #0052d9;
  min-width: 24px;
}

.question-content h3 {
  line-height: 1.4;
  padding-bottom: 4px!important;
  margin-left: 16px!important;
  margin-right: 16px!important;
  margin-top: 8px!important;
  text-align: justify;
  text-justify: inter-ideograph;
  font-size: 22px;
  font-weight: 500;
}

div.option-item{
    font-size: 18px;
    margin-left: 16px!important;
    margin-right: 16px!important;
    text-align: justify;
    line-height: 1.5;
    text-justify: inter-ideograph;
    padding-left: 25px;
    text-indent: -25px;
    margin-bottom: 6px;
}

@media screen and (max-width: 700px) {
  .question-content h3 {
    margin-left: 0!important;
    margin-right: 0!important;
  }
  
  div.option-item {
    margin-left: 0!important;
    margin-right: 0!important;
  }
  
  div.question-stars {
    margin-left: 0!important;
    padding-right: 0!important;
  }
  
  .question-buttons {
    margin-right: 4px;
  }
  
  :deep(.t-dialog) {
    width: 300px !important;
  }
}

.options {
  white-space: pre-wrap;
  margin-bottom: 16px;
}

.analysis {
  margin-top: 16px;
  padding: 16px;
  background-color: rgba(186, 186, 186, 0.1);
  border-radius: 10px;
}

.analysis-container {
  display: flex;
  flex-direction: row;
}

.analysis-content {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.analysis-buttons {
  display: flex;
  flex-direction: column;
  margin-left: 12px;
  align-items: flex-start;
}

.analysis-buttons .t-button {
  padding: 4px;
  margin-bottom: 8px;
}

.analysis-buttons .t-button:last-child {
  margin-bottom: 0;
}

.analysis-buttons .t-button :deep(.t-icon) {
  font-size: 16px;
}

.simple-analysis {
  font-weight: 450;
  font-size: 16px;
}

.detailed-analysis {
  white-space: pre-wrap;
  font-size: 16px;
}

.tab-content {
  padding: 16px;
  height: calc(100vh - 200px);
  overflow-y: auto;
  overflow-x: hidden;
}

.form-list {
  display: flex;
  flex-direction: column;
  gap: 24px;
  height: 100%;
}

.form-item {
  display: flex;
  flex-direction: column;
}

.form-item:first-child {
  flex: 1;
}

.question-textarea {
  flex: 1;
  display: flex;
  flex-direction: column;
}

:deep(.t-textarea__inner) {
  flex: 1;
  min-height: 0 !important;
  margin: 0 !important;
}

.label {
  color: #333;
  font-size: 14px;
  display: block;
  margin-bottom: 8px;
}

.drawer-footer {
  position: absolute;
  bottom: 0;
  left: 0;
  right: 0;
  padding: 16px;
  border-top: 1px;
  display: flex;
  justify-content: space-between;
}

:deep(.t-tabs__nav) {
  padding: 0 16px;
}

:deep(.t-tabs__nav-item) {
  padding: 12px 0;
}

:deep(.t-tabs__nav-item.t-is-active) {
  color: #0052d9;
}

:deep(.t-tabs__nav-item.t-is-active::after) {
  background-color: #0052d9;
}

:deep(.select-popup) {
  border-radius: 4px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}

.question-stars {
  margin-top: 16px;
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding-right: 0;
  margin-left: 16px;
}

.question-buttons {
  display: flex;
  gap: 4px;
  margin-left: auto;
}

.paper-list-header {
  display: flex;
  justify-content: flex-end;
  padding: 16px;
  border-bottom: 1px solid #f0f0f0;
}

.add-paper-text {
  color: #0052d9;
  cursor: pointer;
}

.add-paper-row {
  display: flex;
  align-items: center;
  gap: 16px;
  width: 100%;
}

.add-paper-actions {
  display: flex;
  gap: 8px;
}

.option-item {
  margin-bottom: 8px;
}

.sticky-tool {
  position: fixed !important;
  right: 24px;
  bottom: 24px;
  z-index: 1000;
  user-select: none;
  touch-action: none;
  -webkit-touch-callout: none;
  -webkit-user-select: none;
  -webkit-tap-highlight-color: transparent;
  transform: translate3d(0, 0, 0);
  will-change: transform;
  backface-visibility: hidden;
  perspective: 1000px;
}

:deep(.t-sticky-tool) {
  position: fixed !important;
  right: 24px;
  bottom: 24px;
  z-index: 1000;
  user-select: none;
  touch-action: none;
  -webkit-touch-callout: none;
  -webkit-user-select: none;
  -webkit-tap-highlight-color: transparent;
  transform: translate3d(0, 0, 0);
  will-change: transform;
  backface-visibility: hidden;
  perspective: 1000px;
}

:deep(.t-sticky-item) {
  user-select: none;
  touch-action: none;
  -webkit-touch-callout: none;
  -webkit-user-select: none;
  -webkit-tap-highlight-color: transparent;
}

.drag-handle {
  cursor: move;
  user-select: none;
  touch-action: none;
  position: relative;
  z-index: 1001;
  -webkit-touch-callout: none;
  -webkit-user-select: none;
  -webkit-tap-highlight-color: transparent;
}

.drag-handle :deep(.t-icon) {
  cursor: move;
  display: inline-block;
}

:deep(.t-space.t-space-vertical) {
  width: 100%;
}

:deep(.t-space.t-space-vertical .t-input),
:deep(.t-space.t-space-vertical .t-input-adornment),
:deep(.t-space.t-space-vertical .t-textarea) {
  width: 100%;
}

:deep(.t-input-adornment) {
  width: 100%;
  display: flex;
}

:deep(.t-input-adornment__content) {
  flex: 1;
  width: 0;
  min-width: 0;
}

:deep(.t-input-adornment .t-input) {
  width: 100%;
}

:deep(.t-input__inner) {
  width: 100%;
  text-overflow: ellipsis;
  overflow: hidden;
}

.question-input-header {
  display: flex;
  justify-content: flex-end;
  padding-bottom: 16px;
  padding-top: 0px;
}

.clear-text {
  color: #0052d9;
  cursor: pointer;
  margin-right: 16px;
}

.analysis-header {
  display: flex;
  justify-content: flex-end;
  align-items: center;
  margin-bottom: 8px;
}

.analysis-header h4 {
  margin: 0;
}

.analysis-header .t-button {
  padding: 4px;
}

.analysis-header .t-button :deep(.t-icon) {
  font-size: 16px;
}

:deep(.t-tabs__content) {
  height: 100%;
  overflow: hidden;
}

:deep(.t-tab-panel) {
  height: 100%;
  overflow: hidden;
}

/* 文本转换相关样式 */
.result-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 8px;
}

.result-textarea {
  background-color: #f8f8f8;
  font-family: monospace;
}

.form-actions {
  display: flex;
  justify-content: flex-end;
  margin-top: 16px;
}

/* 添加清除按钮样式 */
.clear-text {
  color: #0052d9;
  cursor: pointer;
}

/* 表格相关样式 */
.table-controls {
  margin-bottom: 12px;
}

.markdown-table-wrapper {
  margin: 16px 0;
  overflow-x: auto;
}

.font-size-slider {
  margin-top: 8px;
  padding: 8px 0;
}

.font-size-demo {
  margin-top: 40px;
  padding: 8px 16px;
  background-color: rgba(0, 0, 0, 0.05);
  border-radius: 4px;
  text-align: center;
  height: 24px;
}

.clear-text.no-underline {
  color: #0052d9;
  cursor: pointer;
}

.clear-text.no-underline:hover {
  text-decoration: none;
}
</style>
  
<style>
p.simple-analysis{
    margin-top: 2px;
    margin-bottom: 0!important;
}

p.detailed-analysis{
    margin-bottom: 0px!important;
    margin: 0;
}

div.analysis{
    margin-top: 16px!important;
}

div.question-stars{
    margin-left: 16px;
    margin-top: 16px!important;
    margin-bottom: 4px!important;

}

/* 为文字透明度变化添加平滑过渡 */
.content-area h3, 
.content-area .option-item, 
.content-area .simple-analysis, 
.content-area .detailed-analysis {
    transition: opacity 0.3s ease;
}

.t-sticky-item {
  display: flex;
  align-items: center;
  justify-content: center;
}

.drag-handle{
    cursor: grab!important;
    user-select: none;
    touch-action: none;
    position: relative;
    z-index: 1001;
}

.sync-button {
    cursor: pointer !important;
    user-select: none;
    touch-action: manipulation;
    position: relative;
    z-index: 1001;
}

.sync-button:active {
    opacity: 0.8;
}

h4.t-typography{
    margin-bottom: 0px!important;
    display: flex;
    align-items: center;
    justify-content: space-between;
}

h4.t-typography .t-button {
    margin-left: auto;
}

.question-content h3{
    line-height: 33px;
    margin-bottom: 11px!important; /* 减小底部边距 */
    margin-left: 16px!important;
    margin-right: 16px!important;
    margin-top: 8px!important;
}

/* 添加连续h3之间的间距样式 */
.question-content h3 + h3 {
    margin-top: 11px !important;
}

/* 当h3后面是选项时的间距 */
.question-content h3 + .option-item {
    margin-top: 8px !important;
}

p{
    font-size: 18px;
}

h6.t-typography{
    color: grey!important;
    margin: 0px!important;
}

</style>